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 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 561 struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) 562 { 563 return drm_gem_fb_create(dev, file_priv, mode_cmd); 564 } 565 566 struct atmel_hlcdc_dc_commit { 567 struct work_struct work; 568 struct drm_device *dev; 569 struct drm_atomic_state *state; 570 }; 571 572 static void 573 atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) 574 { 575 struct drm_device *dev = commit->dev; 576 struct atmel_hlcdc_dc *dc = dev->dev_private; 577 struct drm_atomic_state *old_state = commit->state; 578 579 /* Apply the atomic update. */ 580 drm_atomic_helper_commit_modeset_disables(dev, old_state); 581 drm_atomic_helper_commit_planes(dev, old_state, 0); 582 drm_atomic_helper_commit_modeset_enables(dev, old_state); 583 584 drm_atomic_helper_wait_for_vblanks(dev, old_state); 585 586 drm_atomic_helper_cleanup_planes(dev, old_state); 587 588 drm_atomic_state_put(old_state); 589 590 /* Complete the commit, wake up any waiter. */ 591 spin_lock(&dc->commit.wait.lock); 592 dc->commit.pending = false; 593 wake_up_all_locked(&dc->commit.wait); 594 spin_unlock(&dc->commit.wait.lock); 595 596 kfree(commit); 597 } 598 599 static void atmel_hlcdc_dc_atomic_work(struct work_struct *work) 600 { 601 struct atmel_hlcdc_dc_commit *commit = 602 container_of(work, struct atmel_hlcdc_dc_commit, work); 603 604 atmel_hlcdc_dc_atomic_complete(commit); 605 } 606 607 static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, 608 struct drm_atomic_state *state, 609 bool async) 610 { 611 struct atmel_hlcdc_dc *dc = dev->dev_private; 612 struct atmel_hlcdc_dc_commit *commit; 613 int ret; 614 615 ret = drm_atomic_helper_prepare_planes(dev, state); 616 if (ret) 617 return ret; 618 619 /* Allocate the commit object. */ 620 commit = kzalloc(sizeof(*commit), GFP_KERNEL); 621 if (!commit) { 622 ret = -ENOMEM; 623 goto error; 624 } 625 626 INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work); 627 commit->dev = dev; 628 commit->state = state; 629 630 spin_lock(&dc->commit.wait.lock); 631 ret = wait_event_interruptible_locked(dc->commit.wait, 632 !dc->commit.pending); 633 if (ret == 0) 634 dc->commit.pending = true; 635 spin_unlock(&dc->commit.wait.lock); 636 637 if (ret) 638 goto err_free; 639 640 /* We have our own synchronization through the commit lock. */ 641 BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); 642 643 /* Swap state succeeded, this is the point of no return. */ 644 drm_atomic_state_get(state); 645 if (async) 646 queue_work(dc->wq, &commit->work); 647 else 648 atmel_hlcdc_dc_atomic_complete(commit); 649 650 return 0; 651 652 err_free: 653 kfree(commit); 654 error: 655 drm_atomic_helper_cleanup_planes(dev, state); 656 return ret; 657 } 658 659 static const struct drm_mode_config_funcs mode_config_funcs = { 660 .fb_create = atmel_hlcdc_fb_create, 661 .atomic_check = drm_atomic_helper_check, 662 .atomic_commit = atmel_hlcdc_dc_atomic_commit, 663 }; 664 665 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 666 { 667 struct atmel_hlcdc_dc *dc = dev->dev_private; 668 int ret; 669 670 drm_mode_config_init(dev); 671 672 ret = atmel_hlcdc_create_outputs(dev); 673 if (ret) { 674 dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); 675 return ret; 676 } 677 678 ret = atmel_hlcdc_create_planes(dev); 679 if (ret) { 680 dev_err(dev->dev, "failed to create planes: %d\n", ret); 681 return ret; 682 } 683 684 ret = atmel_hlcdc_crtc_create(dev); 685 if (ret) { 686 dev_err(dev->dev, "failed to create crtc\n"); 687 return ret; 688 } 689 690 dev->mode_config.min_width = dc->desc->min_width; 691 dev->mode_config.min_height = dc->desc->min_height; 692 dev->mode_config.max_width = dc->desc->max_width; 693 dev->mode_config.max_height = dc->desc->max_height; 694 dev->mode_config.funcs = &mode_config_funcs; 695 696 return 0; 697 } 698 699 static int atmel_hlcdc_dc_load(struct drm_device *dev) 700 { 701 struct platform_device *pdev = to_platform_device(dev->dev); 702 const struct of_device_id *match; 703 struct atmel_hlcdc_dc *dc; 704 int ret; 705 706 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 707 if (!match) { 708 dev_err(&pdev->dev, "invalid compatible string\n"); 709 return -ENODEV; 710 } 711 712 if (!match->data) { 713 dev_err(&pdev->dev, "invalid hlcdc description\n"); 714 return -EINVAL; 715 } 716 717 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 718 if (!dc) 719 return -ENOMEM; 720 721 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 722 if (!dc->wq) 723 return -ENOMEM; 724 725 init_waitqueue_head(&dc->commit.wait); 726 dc->desc = match->data; 727 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 728 dev->dev_private = dc; 729 730 if (dc->desc->fixed_clksrc) { 731 ret = clk_prepare_enable(dc->hlcdc->sys_clk); 732 if (ret) { 733 dev_err(dev->dev, "failed to enable sys_clk\n"); 734 goto err_destroy_wq; 735 } 736 } 737 738 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 739 if (ret) { 740 dev_err(dev->dev, "failed to enable periph_clk\n"); 741 goto err_sys_clk_disable; 742 } 743 744 pm_runtime_enable(dev->dev); 745 746 ret = drm_vblank_init(dev, 1); 747 if (ret < 0) { 748 dev_err(dev->dev, "failed to initialize vblank\n"); 749 goto err_periph_clk_disable; 750 } 751 752 ret = atmel_hlcdc_dc_modeset_init(dev); 753 if (ret < 0) { 754 dev_err(dev->dev, "failed to initialize mode setting\n"); 755 goto err_periph_clk_disable; 756 } 757 758 drm_mode_config_reset(dev); 759 760 pm_runtime_get_sync(dev->dev); 761 ret = drm_irq_install(dev, dc->hlcdc->irq); 762 pm_runtime_put_sync(dev->dev); 763 if (ret < 0) { 764 dev_err(dev->dev, "failed to install IRQ handler\n"); 765 goto err_periph_clk_disable; 766 } 767 768 platform_set_drvdata(pdev, dev); 769 770 drm_kms_helper_poll_init(dev); 771 772 return 0; 773 774 err_periph_clk_disable: 775 pm_runtime_disable(dev->dev); 776 clk_disable_unprepare(dc->hlcdc->periph_clk); 777 err_sys_clk_disable: 778 if (dc->desc->fixed_clksrc) 779 clk_disable_unprepare(dc->hlcdc->sys_clk); 780 781 err_destroy_wq: 782 destroy_workqueue(dc->wq); 783 784 return ret; 785 } 786 787 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 788 { 789 struct atmel_hlcdc_dc *dc = dev->dev_private; 790 791 flush_workqueue(dc->wq); 792 drm_kms_helper_poll_fini(dev); 793 drm_atomic_helper_shutdown(dev); 794 drm_mode_config_cleanup(dev); 795 796 pm_runtime_get_sync(dev->dev); 797 drm_irq_uninstall(dev); 798 pm_runtime_put_sync(dev->dev); 799 800 dev->dev_private = NULL; 801 802 pm_runtime_disable(dev->dev); 803 clk_disable_unprepare(dc->hlcdc->periph_clk); 804 if (dc->desc->fixed_clksrc) 805 clk_disable_unprepare(dc->hlcdc->sys_clk); 806 destroy_workqueue(dc->wq); 807 } 808 809 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 810 { 811 struct atmel_hlcdc_dc *dc = dev->dev_private; 812 unsigned int cfg = 0; 813 int i; 814 815 /* Enable interrupts on activated layers */ 816 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 817 if (dc->layers[i]) 818 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 819 } 820 821 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 822 823 return 0; 824 } 825 826 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 827 { 828 struct atmel_hlcdc_dc *dc = dev->dev_private; 829 unsigned int isr; 830 831 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 832 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 833 } 834 835 DEFINE_DRM_GEM_CMA_FOPS(fops); 836 837 static struct drm_driver atmel_hlcdc_dc_driver = { 838 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 839 .irq_handler = atmel_hlcdc_dc_irq_handler, 840 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 841 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 842 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 843 .gem_free_object_unlocked = drm_gem_cma_free_object, 844 .gem_vm_ops = &drm_gem_cma_vm_ops, 845 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 846 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 847 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 848 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 849 .gem_prime_vmap = drm_gem_cma_prime_vmap, 850 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 851 .gem_prime_mmap = drm_gem_cma_prime_mmap, 852 .dumb_create = drm_gem_cma_dumb_create, 853 .fops = &fops, 854 .name = "atmel-hlcdc", 855 .desc = "Atmel HLCD Controller DRM", 856 .date = "20141504", 857 .major = 1, 858 .minor = 0, 859 }; 860 861 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 862 { 863 struct drm_device *ddev; 864 int ret; 865 866 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 867 if (IS_ERR(ddev)) 868 return PTR_ERR(ddev); 869 870 ret = atmel_hlcdc_dc_load(ddev); 871 if (ret) 872 goto err_put; 873 874 ret = drm_dev_register(ddev, 0); 875 if (ret) 876 goto err_unload; 877 878 drm_fbdev_generic_setup(ddev, 24); 879 880 return 0; 881 882 err_unload: 883 atmel_hlcdc_dc_unload(ddev); 884 885 err_put: 886 drm_dev_put(ddev); 887 888 return ret; 889 } 890 891 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 892 { 893 struct drm_device *ddev = platform_get_drvdata(pdev); 894 895 drm_dev_unregister(ddev); 896 atmel_hlcdc_dc_unload(ddev); 897 drm_dev_put(ddev); 898 899 return 0; 900 } 901 902 #ifdef CONFIG_PM_SLEEP 903 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 904 { 905 struct drm_device *drm_dev = dev_get_drvdata(dev); 906 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 907 struct regmap *regmap = dc->hlcdc->regmap; 908 struct drm_atomic_state *state; 909 910 state = drm_atomic_helper_suspend(drm_dev); 911 if (IS_ERR(state)) 912 return PTR_ERR(state); 913 914 dc->suspend.state = state; 915 916 regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); 917 regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); 918 clk_disable_unprepare(dc->hlcdc->periph_clk); 919 if (dc->desc->fixed_clksrc) 920 clk_disable_unprepare(dc->hlcdc->sys_clk); 921 922 return 0; 923 } 924 925 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 926 { 927 struct drm_device *drm_dev = dev_get_drvdata(dev); 928 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 929 930 if (dc->desc->fixed_clksrc) 931 clk_prepare_enable(dc->hlcdc->sys_clk); 932 clk_prepare_enable(dc->hlcdc->periph_clk); 933 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); 934 935 return drm_atomic_helper_resume(drm_dev, dc->suspend.state); 936 } 937 #endif 938 939 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 940 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 941 942 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 943 { .compatible = "atmel,hlcdc-display-controller" }, 944 { }, 945 }; 946 947 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 948 .probe = atmel_hlcdc_dc_drm_probe, 949 .remove = atmel_hlcdc_dc_drm_remove, 950 .driver = { 951 .name = "atmel-hlcdc-display-controller", 952 .pm = &atmel_hlcdc_dc_drm_pm_ops, 953 .of_match_table = atmel_hlcdc_dc_of_match, 954 }, 955 }; 956 module_platform_driver(atmel_hlcdc_dc_platform_driver); 957 958 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 959 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 960 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 961 MODULE_LICENSE("GPL"); 962 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 963