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