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, 0); 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_put(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(state, true); 523 524 drm_atomic_state_get(state); 525 if (async) 526 queue_work(dc->wq, &commit->work); 527 else 528 atmel_hlcdc_dc_atomic_complete(commit); 529 530 return 0; 531 532 error: 533 drm_atomic_helper_cleanup_planes(dev, state); 534 return ret; 535 } 536 537 static const struct drm_mode_config_funcs mode_config_funcs = { 538 .fb_create = atmel_hlcdc_fb_create, 539 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 540 .atomic_check = drm_atomic_helper_check, 541 .atomic_commit = atmel_hlcdc_dc_atomic_commit, 542 }; 543 544 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 545 { 546 struct atmel_hlcdc_dc *dc = dev->dev_private; 547 struct atmel_hlcdc_planes *planes; 548 int ret; 549 int i; 550 551 drm_mode_config_init(dev); 552 553 ret = atmel_hlcdc_create_outputs(dev); 554 if (ret) { 555 dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); 556 return ret; 557 } 558 559 planes = atmel_hlcdc_create_planes(dev); 560 if (IS_ERR(planes)) { 561 dev_err(dev->dev, "failed to create planes\n"); 562 return PTR_ERR(planes); 563 } 564 565 dc->planes = planes; 566 567 dc->layers[planes->primary->layer.desc->id] = 568 &planes->primary->layer; 569 570 if (planes->cursor) 571 dc->layers[planes->cursor->layer.desc->id] = 572 &planes->cursor->layer; 573 574 for (i = 0; i < planes->noverlays; i++) 575 dc->layers[planes->overlays[i]->layer.desc->id] = 576 &planes->overlays[i]->layer; 577 578 ret = atmel_hlcdc_crtc_create(dev); 579 if (ret) { 580 dev_err(dev->dev, "failed to create crtc\n"); 581 return ret; 582 } 583 584 dev->mode_config.min_width = dc->desc->min_width; 585 dev->mode_config.min_height = dc->desc->min_height; 586 dev->mode_config.max_width = dc->desc->max_width; 587 dev->mode_config.max_height = dc->desc->max_height; 588 dev->mode_config.funcs = &mode_config_funcs; 589 590 return 0; 591 } 592 593 static int atmel_hlcdc_dc_load(struct drm_device *dev) 594 { 595 struct platform_device *pdev = to_platform_device(dev->dev); 596 const struct of_device_id *match; 597 struct atmel_hlcdc_dc *dc; 598 int ret; 599 600 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 601 if (!match) { 602 dev_err(&pdev->dev, "invalid compatible string\n"); 603 return -ENODEV; 604 } 605 606 if (!match->data) { 607 dev_err(&pdev->dev, "invalid hlcdc description\n"); 608 return -EINVAL; 609 } 610 611 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 612 if (!dc) 613 return -ENOMEM; 614 615 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 616 if (!dc->wq) 617 return -ENOMEM; 618 619 init_waitqueue_head(&dc->commit.wait); 620 dc->desc = match->data; 621 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 622 dev->dev_private = dc; 623 624 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 625 if (ret) { 626 dev_err(dev->dev, "failed to enable periph_clk\n"); 627 goto err_destroy_wq; 628 } 629 630 pm_runtime_enable(dev->dev); 631 632 ret = drm_vblank_init(dev, 1); 633 if (ret < 0) { 634 dev_err(dev->dev, "failed to initialize vblank\n"); 635 goto err_periph_clk_disable; 636 } 637 638 ret = atmel_hlcdc_dc_modeset_init(dev); 639 if (ret < 0) { 640 dev_err(dev->dev, "failed to initialize mode setting\n"); 641 goto err_periph_clk_disable; 642 } 643 644 drm_mode_config_reset(dev); 645 646 pm_runtime_get_sync(dev->dev); 647 ret = drm_irq_install(dev, dc->hlcdc->irq); 648 pm_runtime_put_sync(dev->dev); 649 if (ret < 0) { 650 dev_err(dev->dev, "failed to install IRQ handler\n"); 651 goto err_periph_clk_disable; 652 } 653 654 platform_set_drvdata(pdev, dev); 655 656 drm_kms_helper_poll_init(dev); 657 658 /* force connectors detection */ 659 drm_helper_hpd_irq_event(dev); 660 661 return 0; 662 663 err_periph_clk_disable: 664 pm_runtime_disable(dev->dev); 665 clk_disable_unprepare(dc->hlcdc->periph_clk); 666 667 err_destroy_wq: 668 destroy_workqueue(dc->wq); 669 670 return ret; 671 } 672 673 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 674 { 675 struct atmel_hlcdc_dc *dc = dev->dev_private; 676 677 if (dc->fbdev) 678 drm_fbdev_cma_fini(dc->fbdev); 679 flush_workqueue(dc->wq); 680 drm_kms_helper_poll_fini(dev); 681 drm_mode_config_cleanup(dev); 682 drm_vblank_cleanup(dev); 683 684 pm_runtime_get_sync(dev->dev); 685 drm_irq_uninstall(dev); 686 pm_runtime_put_sync(dev->dev); 687 688 dev->dev_private = NULL; 689 690 pm_runtime_disable(dev->dev); 691 clk_disable_unprepare(dc->hlcdc->periph_clk); 692 destroy_workqueue(dc->wq); 693 } 694 695 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 696 { 697 struct atmel_hlcdc_dc *dc = dev->dev_private; 698 699 drm_fbdev_cma_restore_mode(dc->fbdev); 700 } 701 702 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 703 { 704 struct atmel_hlcdc_dc *dc = dev->dev_private; 705 unsigned int cfg = 0; 706 int i; 707 708 /* Enable interrupts on activated layers */ 709 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 710 if (dc->layers[i]) 711 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 712 } 713 714 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 715 716 return 0; 717 } 718 719 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 720 { 721 struct atmel_hlcdc_dc *dc = dev->dev_private; 722 unsigned int isr; 723 724 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 725 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 726 } 727 728 static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, 729 unsigned int pipe) 730 { 731 struct atmel_hlcdc_dc *dc = dev->dev_private; 732 733 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 734 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 735 736 return 0; 737 } 738 739 static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, 740 unsigned int pipe) 741 { 742 struct atmel_hlcdc_dc *dc = dev->dev_private; 743 744 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 745 } 746 747 static const struct file_operations fops = { 748 .owner = THIS_MODULE, 749 .open = drm_open, 750 .release = drm_release, 751 .unlocked_ioctl = drm_ioctl, 752 .compat_ioctl = drm_compat_ioctl, 753 .poll = drm_poll, 754 .read = drm_read, 755 .llseek = no_llseek, 756 .mmap = drm_gem_cma_mmap, 757 }; 758 759 static struct drm_driver atmel_hlcdc_dc_driver = { 760 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 761 DRIVER_MODESET | DRIVER_PRIME | 762 DRIVER_ATOMIC, 763 .lastclose = atmel_hlcdc_dc_lastclose, 764 .irq_handler = atmel_hlcdc_dc_irq_handler, 765 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 766 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 767 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 768 .get_vblank_counter = drm_vblank_no_hw_counter, 769 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 770 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 771 .gem_free_object_unlocked = drm_gem_cma_free_object, 772 .gem_vm_ops = &drm_gem_cma_vm_ops, 773 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 774 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 775 .gem_prime_import = drm_gem_prime_import, 776 .gem_prime_export = drm_gem_prime_export, 777 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 778 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 779 .gem_prime_vmap = drm_gem_cma_prime_vmap, 780 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 781 .gem_prime_mmap = drm_gem_cma_prime_mmap, 782 .dumb_create = drm_gem_cma_dumb_create, 783 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 784 .dumb_destroy = drm_gem_dumb_destroy, 785 .fops = &fops, 786 .name = "atmel-hlcdc", 787 .desc = "Atmel HLCD Controller DRM", 788 .date = "20141504", 789 .major = 1, 790 .minor = 0, 791 }; 792 793 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 794 { 795 struct drm_device *ddev; 796 int ret; 797 798 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 799 if (IS_ERR(ddev)) 800 return PTR_ERR(ddev); 801 802 ret = atmel_hlcdc_dc_load(ddev); 803 if (ret) 804 goto err_unref; 805 806 ret = drm_dev_register(ddev, 0); 807 if (ret) 808 goto err_unload; 809 810 return 0; 811 812 err_unload: 813 atmel_hlcdc_dc_unload(ddev); 814 815 err_unref: 816 drm_dev_unref(ddev); 817 818 return ret; 819 } 820 821 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 822 { 823 struct drm_device *ddev = platform_get_drvdata(pdev); 824 825 drm_dev_unregister(ddev); 826 atmel_hlcdc_dc_unload(ddev); 827 drm_dev_unref(ddev); 828 829 return 0; 830 } 831 832 #ifdef CONFIG_PM_SLEEP 833 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 834 { 835 struct drm_device *drm_dev = dev_get_drvdata(dev); 836 struct drm_crtc *crtc; 837 838 if (pm_runtime_suspended(dev)) 839 return 0; 840 841 drm_modeset_lock_all(drm_dev); 842 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 843 atmel_hlcdc_crtc_suspend(crtc); 844 drm_modeset_unlock_all(drm_dev); 845 return 0; 846 } 847 848 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 849 { 850 struct drm_device *drm_dev = dev_get_drvdata(dev); 851 struct drm_crtc *crtc; 852 853 if (pm_runtime_suspended(dev)) 854 return 0; 855 856 drm_modeset_lock_all(drm_dev); 857 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 858 atmel_hlcdc_crtc_resume(crtc); 859 drm_modeset_unlock_all(drm_dev); 860 return 0; 861 } 862 #endif 863 864 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 865 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 866 867 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 868 { .compatible = "atmel,hlcdc-display-controller" }, 869 { }, 870 }; 871 872 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 873 .probe = atmel_hlcdc_dc_drm_probe, 874 .remove = atmel_hlcdc_dc_drm_remove, 875 .driver = { 876 .name = "atmel-hlcdc-display-controller", 877 .pm = &atmel_hlcdc_dc_drm_pm_ops, 878 .of_match_table = atmel_hlcdc_dc_of_match, 879 }, 880 }; 881 module_platform_driver(atmel_hlcdc_dc_platform_driver); 882 883 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 884 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 885 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 886 MODULE_LICENSE("GPL"); 887 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 888