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