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