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