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