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 32static 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 48static 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 57static 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 132static 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 141static 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 235static 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 244static 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 318static 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}; 326static 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,sama5d3-hlcdc", 337 .data = &atmel_hlcdc_dc_sama5d3, 338 }, 339 { 340 .compatible = "atmel,sama5d4-hlcdc", 341 .data = &atmel_hlcdc_dc_sama5d4, 342 }, 343 { /* sentinel */ }, 344}; 345 346int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 347 struct drm_display_mode *mode) 348{ 349 int vfront_porch = mode->vsync_start - mode->vdisplay; 350 int vback_porch = mode->vtotal - mode->vsync_end; 351 int vsync_len = mode->vsync_end - mode->vsync_start; 352 int hfront_porch = mode->hsync_start - mode->hdisplay; 353 int hback_porch = mode->htotal - mode->hsync_end; 354 int hsync_len = mode->hsync_end - mode->hsync_start; 355 356 if (hsync_len > 0x40 || hsync_len < 1) 357 return MODE_HSYNC; 358 359 if (vsync_len > 0x40 || vsync_len < 1) 360 return MODE_VSYNC; 361 362 if (hfront_porch > 0x200 || hfront_porch < 1 || 363 hback_porch > 0x200 || hback_porch < 1 || 364 mode->hdisplay < 1) 365 return MODE_H_ILLEGAL; 366 367 if (vfront_porch > 0x40 || vfront_porch < 1 || 368 vback_porch > 0x40 || vback_porch < 0 || 369 mode->vdisplay < 1) 370 return MODE_V_ILLEGAL; 371 372 return MODE_OK; 373} 374 375static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 376{ 377 struct drm_device *dev = data; 378 struct atmel_hlcdc_dc *dc = dev->dev_private; 379 unsigned long status; 380 unsigned int imr, isr; 381 int i; 382 383 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 384 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 385 status = imr & isr; 386 if (!status) 387 return IRQ_NONE; 388 389 if (status & ATMEL_HLCDC_SOF) 390 atmel_hlcdc_crtc_irq(dc->crtc); 391 392 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 393 struct atmel_hlcdc_layer *layer = dc->layers[i]; 394 395 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer) 396 continue; 397 398 atmel_hlcdc_layer_irq(layer); 399 } 400 401 return IRQ_HANDLED; 402} 403 404static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 405 struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) 406{ 407 return drm_fb_cma_create(dev, file_priv, mode_cmd); 408} 409 410static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 411{ 412 struct atmel_hlcdc_dc *dc = dev->dev_private; 413 414 if (dc->fbdev) { 415 drm_fbdev_cma_hotplug_event(dc->fbdev); 416 } else { 417 dc->fbdev = drm_fbdev_cma_init(dev, 24, 418 dev->mode_config.num_crtc, 419 dev->mode_config.num_connector); 420 if (IS_ERR(dc->fbdev)) 421 dc->fbdev = NULL; 422 } 423} 424 425static const struct drm_mode_config_funcs mode_config_funcs = { 426 .fb_create = atmel_hlcdc_fb_create, 427 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 428 .atomic_check = drm_atomic_helper_check, 429 .atomic_commit = drm_atomic_helper_commit, 430}; 431 432static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 433{ 434 struct atmel_hlcdc_dc *dc = dev->dev_private; 435 struct atmel_hlcdc_planes *planes; 436 int ret; 437 int i; 438 439 drm_mode_config_init(dev); 440 441 ret = atmel_hlcdc_create_outputs(dev); 442 if (ret) { 443 dev_err(dev->dev, "failed to create panel: %d\n", ret); 444 return ret; 445 } 446 447 planes = atmel_hlcdc_create_planes(dev); 448 if (IS_ERR(planes)) { 449 dev_err(dev->dev, "failed to create planes\n"); 450 return PTR_ERR(planes); 451 } 452 453 dc->planes = planes; 454 455 dc->layers[planes->primary->layer.desc->id] = 456 &planes->primary->layer; 457 458 if (planes->cursor) 459 dc->layers[planes->cursor->layer.desc->id] = 460 &planes->cursor->layer; 461 462 for (i = 0; i < planes->noverlays; i++) 463 dc->layers[planes->overlays[i]->layer.desc->id] = 464 &planes->overlays[i]->layer; 465 466 ret = atmel_hlcdc_crtc_create(dev); 467 if (ret) { 468 dev_err(dev->dev, "failed to create crtc\n"); 469 return ret; 470 } 471 472 dev->mode_config.min_width = dc->desc->min_width; 473 dev->mode_config.min_height = dc->desc->min_height; 474 dev->mode_config.max_width = dc->desc->max_width; 475 dev->mode_config.max_height = dc->desc->max_height; 476 dev->mode_config.funcs = &mode_config_funcs; 477 478 return 0; 479} 480 481static int atmel_hlcdc_dc_load(struct drm_device *dev) 482{ 483 struct platform_device *pdev = to_platform_device(dev->dev); 484 const struct of_device_id *match; 485 struct atmel_hlcdc_dc *dc; 486 int ret; 487 488 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 489 if (!match) { 490 dev_err(&pdev->dev, "invalid compatible string\n"); 491 return -ENODEV; 492 } 493 494 if (!match->data) { 495 dev_err(&pdev->dev, "invalid hlcdc description\n"); 496 return -EINVAL; 497 } 498 499 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 500 if (!dc) 501 return -ENOMEM; 502 503 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 504 if (!dc->wq) 505 return -ENOMEM; 506 507 dc->desc = match->data; 508 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 509 dev->dev_private = dc; 510 511 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 512 if (ret) { 513 dev_err(dev->dev, "failed to enable periph_clk\n"); 514 goto err_destroy_wq; 515 } 516 517 pm_runtime_enable(dev->dev); 518 519 ret = drm_vblank_init(dev, 1); 520 if (ret < 0) { 521 dev_err(dev->dev, "failed to initialize vblank\n"); 522 goto err_periph_clk_disable; 523 } 524 525 ret = atmel_hlcdc_dc_modeset_init(dev); 526 if (ret < 0) { 527 dev_err(dev->dev, "failed to initialize mode setting\n"); 528 goto err_periph_clk_disable; 529 } 530 531 drm_mode_config_reset(dev); 532 533 pm_runtime_get_sync(dev->dev); 534 ret = drm_irq_install(dev, dc->hlcdc->irq); 535 pm_runtime_put_sync(dev->dev); 536 if (ret < 0) { 537 dev_err(dev->dev, "failed to install IRQ handler\n"); 538 goto err_periph_clk_disable; 539 } 540 541 platform_set_drvdata(pdev, dev); 542 543 drm_kms_helper_poll_init(dev); 544 545 /* force connectors detection */ 546 drm_helper_hpd_irq_event(dev); 547 548 return 0; 549 550err_periph_clk_disable: 551 pm_runtime_disable(dev->dev); 552 clk_disable_unprepare(dc->hlcdc->periph_clk); 553 554err_destroy_wq: 555 destroy_workqueue(dc->wq); 556 557 return ret; 558} 559 560static void atmel_hlcdc_dc_unload(struct drm_device *dev) 561{ 562 struct atmel_hlcdc_dc *dc = dev->dev_private; 563 564 if (dc->fbdev) 565 drm_fbdev_cma_fini(dc->fbdev); 566 flush_workqueue(dc->wq); 567 drm_kms_helper_poll_fini(dev); 568 drm_mode_config_cleanup(dev); 569 drm_vblank_cleanup(dev); 570 571 pm_runtime_get_sync(dev->dev); 572 drm_irq_uninstall(dev); 573 pm_runtime_put_sync(dev->dev); 574 575 dev->dev_private = NULL; 576 577 pm_runtime_disable(dev->dev); 578 clk_disable_unprepare(dc->hlcdc->periph_clk); 579 destroy_workqueue(dc->wq); 580} 581 582static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 583{ 584 struct drm_connector *connector, *failed; 585 int ret; 586 587 mutex_lock(&dev->mode_config.mutex); 588 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 589 ret = drm_connector_register(connector); 590 if (ret) { 591 failed = connector; 592 goto err; 593 } 594 } 595 mutex_unlock(&dev->mode_config.mutex); 596 return 0; 597 598err: 599 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 600 if (failed == connector) 601 break; 602 603 drm_connector_unregister(connector); 604 } 605 mutex_unlock(&dev->mode_config.mutex); 606 607 return ret; 608} 609 610static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 611{ 612 mutex_lock(&dev->mode_config.mutex); 613 drm_connector_unplug_all(dev); 614 mutex_unlock(&dev->mode_config.mutex); 615} 616 617static void atmel_hlcdc_dc_preclose(struct drm_device *dev, 618 struct drm_file *file) 619{ 620 struct drm_crtc *crtc; 621 622 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 623 atmel_hlcdc_crtc_cancel_page_flip(crtc, file); 624} 625 626static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 627{ 628 struct atmel_hlcdc_dc *dc = dev->dev_private; 629 630 drm_fbdev_cma_restore_mode(dc->fbdev); 631} 632 633static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 634{ 635 struct atmel_hlcdc_dc *dc = dev->dev_private; 636 unsigned int cfg = 0; 637 int i; 638 639 /* Enable interrupts on activated layers */ 640 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 641 if (dc->layers[i]) 642 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 643 } 644 645 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 646 647 return 0; 648} 649 650static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 651{ 652 struct atmel_hlcdc_dc *dc = dev->dev_private; 653 unsigned int isr; 654 655 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 656 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 657} 658 659static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, 660 unsigned int pipe) 661{ 662 struct atmel_hlcdc_dc *dc = dev->dev_private; 663 664 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 665 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 666 667 return 0; 668} 669 670static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, 671 unsigned int pipe) 672{ 673 struct atmel_hlcdc_dc *dc = dev->dev_private; 674 675 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 676} 677 678static const struct file_operations fops = { 679 .owner = THIS_MODULE, 680 .open = drm_open, 681 .release = drm_release, 682 .unlocked_ioctl = drm_ioctl, 683#ifdef CONFIG_COMPAT 684 .compat_ioctl = drm_compat_ioctl, 685#endif 686 .poll = drm_poll, 687 .read = drm_read, 688 .llseek = no_llseek, 689 .mmap = drm_gem_cma_mmap, 690}; 691 692static struct drm_driver atmel_hlcdc_dc_driver = { 693 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 694 DRIVER_MODESET | DRIVER_PRIME | 695 DRIVER_ATOMIC, 696 .preclose = atmel_hlcdc_dc_preclose, 697 .lastclose = atmel_hlcdc_dc_lastclose, 698 .irq_handler = atmel_hlcdc_dc_irq_handler, 699 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 700 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 701 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 702 .get_vblank_counter = drm_vblank_no_hw_counter, 703 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 704 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 705 .gem_free_object = drm_gem_cma_free_object, 706 .gem_vm_ops = &drm_gem_cma_vm_ops, 707 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 708 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 709 .gem_prime_import = drm_gem_prime_import, 710 .gem_prime_export = drm_gem_prime_export, 711 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 712 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 713 .gem_prime_vmap = drm_gem_cma_prime_vmap, 714 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 715 .gem_prime_mmap = drm_gem_cma_prime_mmap, 716 .dumb_create = drm_gem_cma_dumb_create, 717 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 718 .dumb_destroy = drm_gem_dumb_destroy, 719 .fops = &fops, 720 .name = "atmel-hlcdc", 721 .desc = "Atmel HLCD Controller DRM", 722 .date = "20141504", 723 .major = 1, 724 .minor = 0, 725}; 726 727static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 728{ 729 struct drm_device *ddev; 730 int ret; 731 732 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 733 if (!ddev) 734 return -ENOMEM; 735 736 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev)); 737 if (ret) 738 goto err_unref; 739 740 ret = atmel_hlcdc_dc_load(ddev); 741 if (ret) 742 goto err_unref; 743 744 ret = drm_dev_register(ddev, 0); 745 if (ret) 746 goto err_unload; 747 748 ret = atmel_hlcdc_dc_connector_plug_all(ddev); 749 if (ret) 750 goto err_unregister; 751 752 return 0; 753 754err_unregister: 755 drm_dev_unregister(ddev); 756 757err_unload: 758 atmel_hlcdc_dc_unload(ddev); 759 760err_unref: 761 drm_dev_unref(ddev); 762 763 return ret; 764} 765 766static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 767{ 768 struct drm_device *ddev = platform_get_drvdata(pdev); 769 770 atmel_hlcdc_dc_connector_unplug_all(ddev); 771 drm_dev_unregister(ddev); 772 atmel_hlcdc_dc_unload(ddev); 773 drm_dev_unref(ddev); 774 775 return 0; 776} 777 778#ifdef CONFIG_PM_SLEEP 779static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 780{ 781 struct drm_device *drm_dev = dev_get_drvdata(dev); 782 struct drm_crtc *crtc; 783 784 if (pm_runtime_suspended(dev)) 785 return 0; 786 787 drm_modeset_lock_all(drm_dev); 788 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 789 atmel_hlcdc_crtc_suspend(crtc); 790 drm_modeset_unlock_all(drm_dev); 791 return 0; 792} 793 794static int atmel_hlcdc_dc_drm_resume(struct device *dev) 795{ 796 struct drm_device *drm_dev = dev_get_drvdata(dev); 797 struct drm_crtc *crtc; 798 799 if (pm_runtime_suspended(dev)) 800 return 0; 801 802 drm_modeset_lock_all(drm_dev); 803 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 804 atmel_hlcdc_crtc_resume(crtc); 805 drm_modeset_unlock_all(drm_dev); 806 return 0; 807} 808#endif 809 810static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 811 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 812 813static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 814 { .compatible = "atmel,hlcdc-display-controller" }, 815 { }, 816}; 817 818static struct platform_driver atmel_hlcdc_dc_platform_driver = { 819 .probe = atmel_hlcdc_dc_drm_probe, 820 .remove = atmel_hlcdc_dc_drm_remove, 821 .driver = { 822 .name = "atmel-hlcdc-display-controller", 823 .pm = &atmel_hlcdc_dc_drm_pm_ops, 824 .of_match_table = atmel_hlcdc_dc_of_match, 825 }, 826}; 827module_platform_driver(atmel_hlcdc_dc_platform_driver); 828 829MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 830MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 831MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 832MODULE_LICENSE("GPL"); 833MODULE_ALIAS("platform:atmel-hlcdc-dc"); 834