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