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