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 .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 static const struct of_device_id atmel_hlcdc_of_match[] = { 368 { 369 .compatible = "atmel,at91sam9n12-hlcdc", 370 .data = &atmel_hlcdc_dc_at91sam9n12, 371 }, 372 { 373 .compatible = "atmel,at91sam9x5-hlcdc", 374 .data = &atmel_hlcdc_dc_at91sam9x5, 375 }, 376 { 377 .compatible = "atmel,sama5d2-hlcdc", 378 .data = &atmel_hlcdc_dc_sama5d4, 379 }, 380 { 381 .compatible = "atmel,sama5d3-hlcdc", 382 .data = &atmel_hlcdc_dc_sama5d3, 383 }, 384 { 385 .compatible = "atmel,sama5d4-hlcdc", 386 .data = &atmel_hlcdc_dc_sama5d4, 387 }, 388 { /* sentinel */ }, 389 }; 390 MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); 391 392 enum drm_mode_status 393 atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 394 const struct drm_display_mode *mode) 395 { 396 int vfront_porch = mode->vsync_start - mode->vdisplay; 397 int vback_porch = mode->vtotal - mode->vsync_end; 398 int vsync_len = mode->vsync_end - mode->vsync_start; 399 int hfront_porch = mode->hsync_start - mode->hdisplay; 400 int hback_porch = mode->htotal - mode->hsync_end; 401 int hsync_len = mode->hsync_end - mode->hsync_start; 402 403 if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1) 404 return MODE_HSYNC; 405 406 if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1) 407 return MODE_VSYNC; 408 409 if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 || 410 hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 || 411 mode->hdisplay < 1) 412 return MODE_H_ILLEGAL; 413 414 if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 || 415 vback_porch > dc->desc->max_vpw || vback_porch < 0 || 416 mode->vdisplay < 1) 417 return MODE_V_ILLEGAL; 418 419 return MODE_OK; 420 } 421 422 static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer) 423 { 424 if (!layer) 425 return; 426 427 if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER || 428 layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER || 429 layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER) 430 atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer)); 431 } 432 433 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 434 { 435 struct drm_device *dev = data; 436 struct atmel_hlcdc_dc *dc = dev->dev_private; 437 unsigned long status; 438 unsigned int imr, isr; 439 int i; 440 441 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 442 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 443 status = imr & isr; 444 if (!status) 445 return IRQ_NONE; 446 447 if (status & ATMEL_HLCDC_SOF) 448 atmel_hlcdc_crtc_irq(dc->crtc); 449 450 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 451 if (ATMEL_HLCDC_LAYER_STATUS(i) & status) 452 atmel_hlcdc_layer_irq(dc->layers[i]); 453 } 454 455 return IRQ_HANDLED; 456 } 457 458 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 459 struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) 460 { 461 return drm_fb_cma_create(dev, file_priv, mode_cmd); 462 } 463 464 static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 465 { 466 struct atmel_hlcdc_dc *dc = dev->dev_private; 467 468 drm_fbdev_cma_hotplug_event(dc->fbdev); 469 } 470 471 struct atmel_hlcdc_dc_commit { 472 struct work_struct work; 473 struct drm_device *dev; 474 struct drm_atomic_state *state; 475 }; 476 477 static void 478 atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) 479 { 480 struct drm_device *dev = commit->dev; 481 struct atmel_hlcdc_dc *dc = dev->dev_private; 482 struct drm_atomic_state *old_state = commit->state; 483 484 /* Apply the atomic update. */ 485 drm_atomic_helper_commit_modeset_disables(dev, old_state); 486 drm_atomic_helper_commit_planes(dev, old_state, 0); 487 drm_atomic_helper_commit_modeset_enables(dev, old_state); 488 489 drm_atomic_helper_wait_for_vblanks(dev, old_state); 490 491 drm_atomic_helper_cleanup_planes(dev, old_state); 492 493 drm_atomic_state_put(old_state); 494 495 /* Complete the commit, wake up any waiter. */ 496 spin_lock(&dc->commit.wait.lock); 497 dc->commit.pending = false; 498 wake_up_all_locked(&dc->commit.wait); 499 spin_unlock(&dc->commit.wait.lock); 500 501 kfree(commit); 502 } 503 504 static void atmel_hlcdc_dc_atomic_work(struct work_struct *work) 505 { 506 struct atmel_hlcdc_dc_commit *commit = 507 container_of(work, struct atmel_hlcdc_dc_commit, work); 508 509 atmel_hlcdc_dc_atomic_complete(commit); 510 } 511 512 static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, 513 struct drm_atomic_state *state, 514 bool async) 515 { 516 struct atmel_hlcdc_dc *dc = dev->dev_private; 517 struct atmel_hlcdc_dc_commit *commit; 518 int ret; 519 520 ret = drm_atomic_helper_prepare_planes(dev, state); 521 if (ret) 522 return ret; 523 524 /* Allocate the commit object. */ 525 commit = kzalloc(sizeof(*commit), GFP_KERNEL); 526 if (!commit) { 527 ret = -ENOMEM; 528 goto error; 529 } 530 531 INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work); 532 commit->dev = dev; 533 commit->state = state; 534 535 spin_lock(&dc->commit.wait.lock); 536 ret = wait_event_interruptible_locked(dc->commit.wait, 537 !dc->commit.pending); 538 if (ret == 0) 539 dc->commit.pending = true; 540 spin_unlock(&dc->commit.wait.lock); 541 542 if (ret) 543 goto err_free; 544 545 /* We have our own synchronization through the commit lock. */ 546 BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); 547 548 /* Swap state succeeded, this is the point of no return. */ 549 drm_atomic_state_get(state); 550 if (async) 551 queue_work(dc->wq, &commit->work); 552 else 553 atmel_hlcdc_dc_atomic_complete(commit); 554 555 return 0; 556 557 err_free: 558 kfree(commit); 559 error: 560 drm_atomic_helper_cleanup_planes(dev, state); 561 return ret; 562 } 563 564 static const struct drm_mode_config_funcs mode_config_funcs = { 565 .fb_create = atmel_hlcdc_fb_create, 566 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 567 .atomic_check = drm_atomic_helper_check, 568 .atomic_commit = atmel_hlcdc_dc_atomic_commit, 569 }; 570 571 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 572 { 573 struct atmel_hlcdc_dc *dc = dev->dev_private; 574 int ret; 575 576 drm_mode_config_init(dev); 577 578 ret = atmel_hlcdc_create_outputs(dev); 579 if (ret) { 580 dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); 581 return ret; 582 } 583 584 ret = atmel_hlcdc_create_planes(dev); 585 if (ret) { 586 dev_err(dev->dev, "failed to create planes: %d\n", ret); 587 return ret; 588 } 589 590 ret = atmel_hlcdc_crtc_create(dev); 591 if (ret) { 592 dev_err(dev->dev, "failed to create crtc\n"); 593 return ret; 594 } 595 596 dev->mode_config.min_width = dc->desc->min_width; 597 dev->mode_config.min_height = dc->desc->min_height; 598 dev->mode_config.max_width = dc->desc->max_width; 599 dev->mode_config.max_height = dc->desc->max_height; 600 dev->mode_config.funcs = &mode_config_funcs; 601 602 return 0; 603 } 604 605 static int atmel_hlcdc_dc_load(struct drm_device *dev) 606 { 607 struct platform_device *pdev = to_platform_device(dev->dev); 608 const struct of_device_id *match; 609 struct atmel_hlcdc_dc *dc; 610 int ret; 611 612 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 613 if (!match) { 614 dev_err(&pdev->dev, "invalid compatible string\n"); 615 return -ENODEV; 616 } 617 618 if (!match->data) { 619 dev_err(&pdev->dev, "invalid hlcdc description\n"); 620 return -EINVAL; 621 } 622 623 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 624 if (!dc) 625 return -ENOMEM; 626 627 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 628 if (!dc->wq) 629 return -ENOMEM; 630 631 init_waitqueue_head(&dc->commit.wait); 632 dc->desc = match->data; 633 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 634 dev->dev_private = dc; 635 636 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 637 if (ret) { 638 dev_err(dev->dev, "failed to enable periph_clk\n"); 639 goto err_destroy_wq; 640 } 641 642 pm_runtime_enable(dev->dev); 643 644 ret = drm_vblank_init(dev, 1); 645 if (ret < 0) { 646 dev_err(dev->dev, "failed to initialize vblank\n"); 647 goto err_periph_clk_disable; 648 } 649 650 ret = atmel_hlcdc_dc_modeset_init(dev); 651 if (ret < 0) { 652 dev_err(dev->dev, "failed to initialize mode setting\n"); 653 goto err_periph_clk_disable; 654 } 655 656 drm_mode_config_reset(dev); 657 658 pm_runtime_get_sync(dev->dev); 659 ret = drm_irq_install(dev, dc->hlcdc->irq); 660 pm_runtime_put_sync(dev->dev); 661 if (ret < 0) { 662 dev_err(dev->dev, "failed to install IRQ handler\n"); 663 goto err_periph_clk_disable; 664 } 665 666 platform_set_drvdata(pdev, dev); 667 668 dc->fbdev = drm_fbdev_cma_init(dev, 24, 669 dev->mode_config.num_connector); 670 if (IS_ERR(dc->fbdev)) 671 dc->fbdev = NULL; 672 673 drm_kms_helper_poll_init(dev); 674 675 return 0; 676 677 err_periph_clk_disable: 678 pm_runtime_disable(dev->dev); 679 clk_disable_unprepare(dc->hlcdc->periph_clk); 680 681 err_destroy_wq: 682 destroy_workqueue(dc->wq); 683 684 return ret; 685 } 686 687 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 688 { 689 struct atmel_hlcdc_dc *dc = dev->dev_private; 690 691 if (dc->fbdev) 692 drm_fbdev_cma_fini(dc->fbdev); 693 flush_workqueue(dc->wq); 694 drm_kms_helper_poll_fini(dev); 695 drm_mode_config_cleanup(dev); 696 697 pm_runtime_get_sync(dev->dev); 698 drm_irq_uninstall(dev); 699 pm_runtime_put_sync(dev->dev); 700 701 dev->dev_private = NULL; 702 703 pm_runtime_disable(dev->dev); 704 clk_disable_unprepare(dc->hlcdc->periph_clk); 705 destroy_workqueue(dc->wq); 706 } 707 708 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 709 { 710 struct atmel_hlcdc_dc *dc = dev->dev_private; 711 712 drm_fbdev_cma_restore_mode(dc->fbdev); 713 } 714 715 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 716 { 717 struct atmel_hlcdc_dc *dc = dev->dev_private; 718 unsigned int cfg = 0; 719 int i; 720 721 /* Enable interrupts on activated layers */ 722 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 723 if (dc->layers[i]) 724 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 725 } 726 727 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 728 729 return 0; 730 } 731 732 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 733 { 734 struct atmel_hlcdc_dc *dc = dev->dev_private; 735 unsigned int isr; 736 737 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 738 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 739 } 740 741 DEFINE_DRM_GEM_CMA_FOPS(fops); 742 743 static struct drm_driver atmel_hlcdc_dc_driver = { 744 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 745 DRIVER_MODESET | DRIVER_PRIME | 746 DRIVER_ATOMIC, 747 .lastclose = atmel_hlcdc_dc_lastclose, 748 .irq_handler = atmel_hlcdc_dc_irq_handler, 749 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 750 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 751 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 752 .gem_free_object_unlocked = drm_gem_cma_free_object, 753 .gem_vm_ops = &drm_gem_cma_vm_ops, 754 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 755 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 756 .gem_prime_import = drm_gem_prime_import, 757 .gem_prime_export = drm_gem_prime_export, 758 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 759 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 760 .gem_prime_vmap = drm_gem_cma_prime_vmap, 761 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 762 .gem_prime_mmap = drm_gem_cma_prime_mmap, 763 .dumb_create = drm_gem_cma_dumb_create, 764 .fops = &fops, 765 .name = "atmel-hlcdc", 766 .desc = "Atmel HLCD Controller DRM", 767 .date = "20141504", 768 .major = 1, 769 .minor = 0, 770 }; 771 772 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 773 { 774 struct drm_device *ddev; 775 int ret; 776 777 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 778 if (IS_ERR(ddev)) 779 return PTR_ERR(ddev); 780 781 ret = atmel_hlcdc_dc_load(ddev); 782 if (ret) 783 goto err_unref; 784 785 ret = drm_dev_register(ddev, 0); 786 if (ret) 787 goto err_unload; 788 789 return 0; 790 791 err_unload: 792 atmel_hlcdc_dc_unload(ddev); 793 794 err_unref: 795 drm_dev_unref(ddev); 796 797 return ret; 798 } 799 800 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 801 { 802 struct drm_device *ddev = platform_get_drvdata(pdev); 803 804 drm_dev_unregister(ddev); 805 atmel_hlcdc_dc_unload(ddev); 806 drm_dev_unref(ddev); 807 808 return 0; 809 } 810 811 #ifdef CONFIG_PM_SLEEP 812 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 813 { 814 struct drm_device *drm_dev = dev_get_drvdata(dev); 815 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 816 struct regmap *regmap = dc->hlcdc->regmap; 817 struct drm_atomic_state *state; 818 819 state = drm_atomic_helper_suspend(drm_dev); 820 if (IS_ERR(state)) 821 return PTR_ERR(state); 822 823 dc->suspend.state = state; 824 825 regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); 826 regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); 827 clk_disable_unprepare(dc->hlcdc->periph_clk); 828 829 return 0; 830 } 831 832 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 833 { 834 struct drm_device *drm_dev = dev_get_drvdata(dev); 835 struct atmel_hlcdc_dc *dc = drm_dev->dev_private; 836 837 clk_prepare_enable(dc->hlcdc->periph_clk); 838 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); 839 840 return drm_atomic_helper_resume(drm_dev, dc->suspend.state); 841 } 842 #endif 843 844 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 845 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 846 847 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 848 { .compatible = "atmel,hlcdc-display-controller" }, 849 { }, 850 }; 851 852 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 853 .probe = atmel_hlcdc_dc_drm_probe, 854 .remove = atmel_hlcdc_dc_drm_remove, 855 .driver = { 856 .name = "atmel-hlcdc-display-controller", 857 .pm = &atmel_hlcdc_dc_drm_pm_ops, 858 .of_match_table = atmel_hlcdc_dc_of_match, 859 }, 860 }; 861 module_platform_driver(atmel_hlcdc_dc_platform_driver); 862 863 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 864 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 865 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 866 MODULE_LICENSE("GPL"); 867 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 868