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