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