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