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