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 enum drm_mode_status 379 atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 380 const struct drm_display_mode *mode) 381 { 382 int vfront_porch = mode->vsync_start - mode->vdisplay; 383 int vback_porch = mode->vtotal - mode->vsync_end; 384 int vsync_len = mode->vsync_end - mode->vsync_start; 385 int hfront_porch = mode->hsync_start - mode->hdisplay; 386 int hback_porch = mode->htotal - mode->hsync_end; 387 int hsync_len = mode->hsync_end - mode->hsync_start; 388 389 if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1) 390 return MODE_HSYNC; 391 392 if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1) 393 return MODE_VSYNC; 394 395 if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 || 396 hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 || 397 mode->hdisplay < 1) 398 return MODE_H_ILLEGAL; 399 400 if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 || 401 vback_porch > dc->desc->max_vpw || vback_porch < 0 || 402 mode->vdisplay < 1) 403 return MODE_V_ILLEGAL; 404 405 return MODE_OK; 406 } 407 408 static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer) 409 { 410 if (!layer) 411 return; 412 413 if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER || 414 layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER || 415 layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER) 416 atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer)); 417 } 418 419 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 420 { 421 struct drm_device *dev = data; 422 struct atmel_hlcdc_dc *dc = dev->dev_private; 423 unsigned long status; 424 unsigned int imr, isr; 425 int i; 426 427 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 428 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 429 status = imr & isr; 430 if (!status) 431 return IRQ_NONE; 432 433 if (status & ATMEL_HLCDC_SOF) 434 atmel_hlcdc_crtc_irq(dc->crtc); 435 436 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 437 if (ATMEL_HLCDC_LAYER_STATUS(i) & status) 438 atmel_hlcdc_layer_irq(dc->layers[i]); 439 } 440 441 return IRQ_HANDLED; 442 } 443 444 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 445 struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) 446 { 447 return drm_fb_cma_create(dev, file_priv, mode_cmd); 448 } 449 450 static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 451 { 452 struct atmel_hlcdc_dc *dc = dev->dev_private; 453 454 if (dc->fbdev) 455 drm_fbdev_cma_hotplug_event(dc->fbdev); 456 } 457 458 struct atmel_hlcdc_dc_commit { 459 struct work_struct work; 460 struct drm_device *dev; 461 struct drm_atomic_state *state; 462 }; 463 464 static void 465 atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) 466 { 467 struct drm_device *dev = commit->dev; 468 struct atmel_hlcdc_dc *dc = dev->dev_private; 469 struct drm_atomic_state *old_state = commit->state; 470 471 /* Apply the atomic update. */ 472 drm_atomic_helper_commit_modeset_disables(dev, old_state); 473 drm_atomic_helper_commit_planes(dev, old_state, 0); 474 drm_atomic_helper_commit_modeset_enables(dev, old_state); 475 476 drm_atomic_helper_wait_for_vblanks(dev, old_state); 477 478 drm_atomic_helper_cleanup_planes(dev, old_state); 479 480 drm_atomic_state_put(old_state); 481 482 /* Complete the commit, wake up any waiter. */ 483 spin_lock(&dc->commit.wait.lock); 484 dc->commit.pending = false; 485 wake_up_all_locked(&dc->commit.wait); 486 spin_unlock(&dc->commit.wait.lock); 487 488 kfree(commit); 489 } 490 491 static void atmel_hlcdc_dc_atomic_work(struct work_struct *work) 492 { 493 struct atmel_hlcdc_dc_commit *commit = 494 container_of(work, struct atmel_hlcdc_dc_commit, work); 495 496 atmel_hlcdc_dc_atomic_complete(commit); 497 } 498 499 static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, 500 struct drm_atomic_state *state, 501 bool async) 502 { 503 struct atmel_hlcdc_dc *dc = dev->dev_private; 504 struct atmel_hlcdc_dc_commit *commit; 505 int ret; 506 507 ret = drm_atomic_helper_prepare_planes(dev, state); 508 if (ret) 509 return ret; 510 511 /* Allocate the commit object. */ 512 commit = kzalloc(sizeof(*commit), GFP_KERNEL); 513 if (!commit) { 514 ret = -ENOMEM; 515 goto error; 516 } 517 518 INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work); 519 commit->dev = dev; 520 commit->state = state; 521 522 spin_lock(&dc->commit.wait.lock); 523 ret = wait_event_interruptible_locked(dc->commit.wait, 524 !dc->commit.pending); 525 if (ret == 0) 526 dc->commit.pending = true; 527 spin_unlock(&dc->commit.wait.lock); 528 529 if (ret) { 530 kfree(commit); 531 goto error; 532 } 533 534 /* Swap the state, this is the point of no return. */ 535 drm_atomic_helper_swap_state(state, true); 536 537 drm_atomic_state_get(state); 538 if (async) 539 queue_work(dc->wq, &commit->work); 540 else 541 atmel_hlcdc_dc_atomic_complete(commit); 542 543 return 0; 544 545 error: 546 drm_atomic_helper_cleanup_planes(dev, state); 547 return ret; 548 } 549 550 static const struct drm_mode_config_funcs mode_config_funcs = { 551 .fb_create = atmel_hlcdc_fb_create, 552 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 553 .atomic_check = drm_atomic_helper_check, 554 .atomic_commit = atmel_hlcdc_dc_atomic_commit, 555 }; 556 557 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 558 { 559 struct atmel_hlcdc_dc *dc = dev->dev_private; 560 int ret; 561 562 drm_mode_config_init(dev); 563 564 ret = atmel_hlcdc_create_outputs(dev); 565 if (ret) { 566 dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); 567 return ret; 568 } 569 570 ret = atmel_hlcdc_create_planes(dev); 571 if (ret) { 572 dev_err(dev->dev, "failed to create planes: %d\n", ret); 573 return ret; 574 } 575 576 ret = atmel_hlcdc_crtc_create(dev); 577 if (ret) { 578 dev_err(dev->dev, "failed to create crtc\n"); 579 return ret; 580 } 581 582 dev->mode_config.min_width = dc->desc->min_width; 583 dev->mode_config.min_height = dc->desc->min_height; 584 dev->mode_config.max_width = dc->desc->max_width; 585 dev->mode_config.max_height = dc->desc->max_height; 586 dev->mode_config.funcs = &mode_config_funcs; 587 588 return 0; 589 } 590 591 static int atmel_hlcdc_dc_load(struct drm_device *dev) 592 { 593 struct platform_device *pdev = to_platform_device(dev->dev); 594 const struct of_device_id *match; 595 struct atmel_hlcdc_dc *dc; 596 int ret; 597 598 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 599 if (!match) { 600 dev_err(&pdev->dev, "invalid compatible string\n"); 601 return -ENODEV; 602 } 603 604 if (!match->data) { 605 dev_err(&pdev->dev, "invalid hlcdc description\n"); 606 return -EINVAL; 607 } 608 609 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 610 if (!dc) 611 return -ENOMEM; 612 613 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 614 if (!dc->wq) 615 return -ENOMEM; 616 617 init_waitqueue_head(&dc->commit.wait); 618 dc->desc = match->data; 619 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 620 dev->dev_private = dc; 621 622 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 623 if (ret) { 624 dev_err(dev->dev, "failed to enable periph_clk\n"); 625 goto err_destroy_wq; 626 } 627 628 pm_runtime_enable(dev->dev); 629 630 ret = drm_vblank_init(dev, 1); 631 if (ret < 0) { 632 dev_err(dev->dev, "failed to initialize vblank\n"); 633 goto err_periph_clk_disable; 634 } 635 636 ret = atmel_hlcdc_dc_modeset_init(dev); 637 if (ret < 0) { 638 dev_err(dev->dev, "failed to initialize mode setting\n"); 639 goto err_periph_clk_disable; 640 } 641 642 drm_mode_config_reset(dev); 643 644 pm_runtime_get_sync(dev->dev); 645 ret = drm_irq_install(dev, dc->hlcdc->irq); 646 pm_runtime_put_sync(dev->dev); 647 if (ret < 0) { 648 dev_err(dev->dev, "failed to install IRQ handler\n"); 649 goto err_periph_clk_disable; 650 } 651 652 platform_set_drvdata(pdev, dev); 653 654 dc->fbdev = drm_fbdev_cma_init(dev, 24, 655 dev->mode_config.num_connector); 656 if (IS_ERR(dc->fbdev)) 657 dc->fbdev = NULL; 658 659 drm_kms_helper_poll_init(dev); 660 661 return 0; 662 663 err_periph_clk_disable: 664 pm_runtime_disable(dev->dev); 665 clk_disable_unprepare(dc->hlcdc->periph_clk); 666 667 err_destroy_wq: 668 destroy_workqueue(dc->wq); 669 670 return ret; 671 } 672 673 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 674 { 675 struct atmel_hlcdc_dc *dc = dev->dev_private; 676 677 if (dc->fbdev) 678 drm_fbdev_cma_fini(dc->fbdev); 679 flush_workqueue(dc->wq); 680 drm_kms_helper_poll_fini(dev); 681 drm_mode_config_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