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