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 of_device_id atmel_hlcdc_of_match[] = { 570 { 571 .compatible = "atmel,at91sam9n12-hlcdc", 572 .data = &atmel_hlcdc_dc_at91sam9n12, 573 }, 574 { 575 .compatible = "atmel,at91sam9x5-hlcdc", 576 .data = &atmel_hlcdc_dc_at91sam9x5, 577 }, 578 { 579 .compatible = "atmel,sama5d2-hlcdc", 580 .data = &atmel_hlcdc_dc_sama5d4, 581 }, 582 { 583 .compatible = "atmel,sama5d3-hlcdc", 584 .data = &atmel_hlcdc_dc_sama5d3, 585 }, 586 { 587 .compatible = "atmel,sama5d4-hlcdc", 588 .data = &atmel_hlcdc_dc_sama5d4, 589 }, 590 { 591 .compatible = "microchip,sam9x60-hlcdc", 592 .data = &atmel_hlcdc_dc_sam9x60, 593 }, 594 { 595 .compatible = "microchip,sam9x75-xlcdc", 596 .data = &atmel_xlcdc_dc_sam9x75, 597 }, 598 { /* sentinel */ }, 599 }; 600 MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); 601 602 enum drm_mode_status 603 atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 604 const struct drm_display_mode *mode) 605 { 606 int vfront_porch = mode->vsync_start - mode->vdisplay; 607 int vback_porch = mode->vtotal - mode->vsync_end; 608 int vsync_len = mode->vsync_end - mode->vsync_start; 609 int hfront_porch = mode->hsync_start - mode->hdisplay; 610 int hback_porch = mode->htotal - mode->hsync_end; 611 int hsync_len = mode->hsync_end - mode->hsync_start; 612 613 if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1) 614 return MODE_HSYNC; 615 616 if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1) 617 return MODE_VSYNC; 618 619 if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 || 620 hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 || 621 mode->hdisplay < 1) 622 return MODE_H_ILLEGAL; 623 624 if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 || 625 vback_porch > dc->desc->max_vpw || vback_porch < 0 || 626 mode->vdisplay < 1) 627 return MODE_V_ILLEGAL; 628 629 return MODE_OK; 630 } 631 632 static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer) 633 { 634 if (!layer) 635 return; 636 637 if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER || 638 layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER || 639 layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER) 640 atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer)); 641 } 642 643 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 644 { 645 struct drm_device *dev = data; 646 struct atmel_hlcdc_dc *dc = dev->dev_private; 647 unsigned long status; 648 unsigned int imr, isr; 649 int i; 650 651 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 652 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 653 status = imr & isr; 654 if (!status) 655 return IRQ_NONE; 656 657 if (status & ATMEL_HLCDC_SOF) 658 atmel_hlcdc_crtc_irq(dc->crtc); 659 660 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 661 if (ATMEL_HLCDC_LAYER_STATUS(i) & status) 662 atmel_hlcdc_layer_irq(dc->layers[i]); 663 } 664 665 return IRQ_HANDLED; 666 } 667 668 static void atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 669 { 670 struct atmel_hlcdc_dc *dc = dev->dev_private; 671 unsigned int cfg = 0; 672 int i; 673 674 /* Enable interrupts on activated layers */ 675 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 676 if (dc->layers[i]) 677 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 678 } 679 680 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 681 } 682 683 static void atmel_hlcdc_dc_irq_disable(struct drm_device *dev) 684 { 685 struct atmel_hlcdc_dc *dc = dev->dev_private; 686 unsigned int isr; 687 688 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 689 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 690 } 691 692 static int atmel_hlcdc_dc_irq_install(struct drm_device *dev, unsigned int irq) 693 { 694 int ret; 695 696 atmel_hlcdc_dc_irq_disable(dev); 697 698 ret = devm_request_irq(dev->dev, irq, atmel_hlcdc_dc_irq_handler, 0, 699 dev->driver->name, dev); 700 if (ret) 701 return ret; 702 703 atmel_hlcdc_dc_irq_postinstall(dev); 704 705 return 0; 706 } 707 708 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 709 { 710 atmel_hlcdc_dc_irq_disable(dev); 711 } 712 713 static const struct drm_mode_config_funcs mode_config_funcs = { 714 .fb_create = drm_gem_fb_create, 715 .atomic_check = drm_atomic_helper_check, 716 .atomic_commit = drm_atomic_helper_commit, 717 }; 718 719 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 720 { 721 struct atmel_hlcdc_dc *dc = dev->dev_private; 722 int ret; 723 724 drm_mode_config_init(dev); 725 726 ret = atmel_hlcdc_create_outputs(dev); 727 if (ret) { 728 drm_err(dev, "failed to create HLCDC outputs: %d\n", ret); 729 return ret; 730 } 731 732 ret = atmel_hlcdc_create_planes(dev); 733 if (ret) { 734 drm_err(dev, "failed to create planes: %d\n", ret); 735 return ret; 736 } 737 738 ret = atmel_hlcdc_crtc_create(dev); 739 if (ret) { 740 drm_err(dev, "failed to create crtc\n"); 741 return ret; 742 } 743 744 dev->mode_config.min_width = dc->desc->min_width; 745 dev->mode_config.min_height = dc->desc->min_height; 746 dev->mode_config.max_width = dc->desc->max_width; 747 dev->mode_config.max_height = dc->desc->max_height; 748 dev->mode_config.funcs = &mode_config_funcs; 749 dev->mode_config.async_page_flip = true; 750 751 return 0; 752 } 753 754 static int atmel_hlcdc_dc_load(struct drm_device *dev) 755 { 756 struct platform_device *pdev = to_platform_device(dev->dev); 757 const struct of_device_id *match; 758 struct atmel_hlcdc_dc *dc; 759 int ret; 760 761 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 762 if (!match) { 763 dev_err(&pdev->dev, "invalid compatible string\n"); 764 return -ENODEV; 765 } 766 767 if (!match->data) { 768 dev_err(&pdev->dev, "invalid hlcdc description\n"); 769 return -EINVAL; 770 } 771 772 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 773 if (!dc) 774 return -ENOMEM; 775 776 dc->desc = match->data; 777 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 778 dev->dev_private = dc; 779 780 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 781 if (ret) { 782 drm_err(dev, "failed to enable periph_clk\n"); 783 return ret; 784 } 785 786 pm_runtime_enable(dev->dev); 787 788 ret = drm_vblank_init(dev, 1); 789 if (ret < 0) { 790 drm_err(dev, "failed to initialize vblank\n"); 791 goto err_periph_clk_disable; 792 } 793 794 ret = atmel_hlcdc_dc_modeset_init(dev); 795 if (ret < 0) { 796 drm_err(dev, "failed to initialize mode setting\n"); 797 goto err_periph_clk_disable; 798 } 799 800 drm_mode_config_reset(dev); 801 802 pm_runtime_get_sync(dev->dev); 803 ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq); 804 pm_runtime_put_sync(dev->dev); 805 if (ret < 0) { 806 drm_err(dev, "failed to install IRQ handler\n"); 807 goto err_periph_clk_disable; 808 } 809 810 platform_set_drvdata(pdev, dev); 811 812 drm_kms_helper_poll_init(dev); 813 814 return 0; 815 816 err_periph_clk_disable: 817 pm_runtime_disable(dev->dev); 818 clk_disable_unprepare(dc->hlcdc->periph_clk); 819 820 return ret; 821 } 822 823 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 824 { 825 struct atmel_hlcdc_dc *dc = dev->dev_private; 826 827 drm_kms_helper_poll_fini(dev); 828 drm_atomic_helper_shutdown(dev); 829 drm_mode_config_cleanup(dev); 830 831 pm_runtime_get_sync(dev->dev); 832 atmel_hlcdc_dc_irq_uninstall(dev); 833 pm_runtime_put_sync(dev->dev); 834 835 dev->dev_private = NULL; 836 837 pm_runtime_disable(dev->dev); 838 clk_disable_unprepare(dc->hlcdc->periph_clk); 839 } 840 841 DEFINE_DRM_GEM_DMA_FOPS(fops); 842 843 static const struct drm_driver atmel_hlcdc_dc_driver = { 844 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 845 DRM_GEM_DMA_DRIVER_OPS, 846 DRM_FBDEV_DMA_DRIVER_OPS, 847 .fops = &fops, 848 .name = "atmel-hlcdc", 849 .desc = "Atmel HLCD Controller DRM", 850 .major = 1, 851 .minor = 0, 852 }; 853 854 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 855 { 856 struct drm_device *ddev; 857 int ret; 858 859 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 860 if (IS_ERR(ddev)) 861 return PTR_ERR(ddev); 862 863 ret = atmel_hlcdc_dc_load(ddev); 864 if (ret) 865 goto err_put; 866 867 ret = drm_dev_register(ddev, 0); 868 if (ret) 869 goto err_unload; 870 871 drm_client_setup_with_fourcc(ddev, DRM_FORMAT_RGB888); 872 873 return 0; 874 875 err_unload: 876 atmel_hlcdc_dc_unload(ddev); 877 878 err_put: 879 drm_dev_put(ddev); 880 881 return ret; 882 } 883 884 static void atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 885 { 886 struct drm_device *ddev = platform_get_drvdata(pdev); 887 888 drm_dev_unregister(ddev); 889 atmel_hlcdc_dc_unload(ddev); 890 drm_dev_put(ddev); 891 } 892 893 static void atmel_hlcdc_dc_drm_shutdown(struct platform_device *pdev) 894 { 895 drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); 896 } 897 898 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 899 { 900 struct drm_device *drm_dev = dev_get_drvdata(dev); 901 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 902 struct regmap *regmap = dc->hlcdc->regmap; 903 struct drm_atomic_state *state; 904 905 state = drm_atomic_helper_suspend(drm_dev); 906 if (IS_ERR(state)) 907 return PTR_ERR(state); 908 909 dc->suspend.state = state; 910 911 regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); 912 regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); 913 clk_disable_unprepare(dc->hlcdc->periph_clk); 914 915 return 0; 916 } 917 918 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 919 { 920 struct drm_device *drm_dev = dev_get_drvdata(dev); 921 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 922 923 clk_prepare_enable(dc->hlcdc->periph_clk); 924 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); 925 926 return drm_atomic_helper_resume(drm_dev, dc->suspend.state); 927 } 928 929 static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 930 atmel_hlcdc_dc_drm_suspend, 931 atmel_hlcdc_dc_drm_resume); 932 933 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 934 { .compatible = "atmel,hlcdc-display-controller" }, 935 { }, 936 }; 937 938 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 939 .probe = atmel_hlcdc_dc_drm_probe, 940 .remove = atmel_hlcdc_dc_drm_remove, 941 .shutdown = atmel_hlcdc_dc_drm_shutdown, 942 .driver = { 943 .name = "atmel-hlcdc-display-controller", 944 .pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops), 945 .of_match_table = atmel_hlcdc_dc_of_match, 946 }, 947 }; 948 drm_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