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_gem_fb_create(dev, file_priv, mode_cmd); 462 } 463 464 struct atmel_hlcdc_dc_commit { 465 struct work_struct work; 466 struct drm_device *dev; 467 struct drm_atomic_state *state; 468 }; 469 470 static void 471 atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) 472 { 473 struct drm_device *dev = commit->dev; 474 struct atmel_hlcdc_dc *dc = dev->dev_private; 475 struct drm_atomic_state *old_state = commit->state; 476 477 /* Apply the atomic update. */ 478 drm_atomic_helper_commit_modeset_disables(dev, old_state); 479 drm_atomic_helper_commit_planes(dev, old_state, 0); 480 drm_atomic_helper_commit_modeset_enables(dev, old_state); 481 482 drm_atomic_helper_wait_for_vblanks(dev, old_state); 483 484 drm_atomic_helper_cleanup_planes(dev, old_state); 485 486 drm_atomic_state_put(old_state); 487 488 /* Complete the commit, wake up any waiter. */ 489 spin_lock(&dc->commit.wait.lock); 490 dc->commit.pending = false; 491 wake_up_all_locked(&dc->commit.wait); 492 spin_unlock(&dc->commit.wait.lock); 493 494 kfree(commit); 495 } 496 497 static void atmel_hlcdc_dc_atomic_work(struct work_struct *work) 498 { 499 struct atmel_hlcdc_dc_commit *commit = 500 container_of(work, struct atmel_hlcdc_dc_commit, work); 501 502 atmel_hlcdc_dc_atomic_complete(commit); 503 } 504 505 static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, 506 struct drm_atomic_state *state, 507 bool async) 508 { 509 struct atmel_hlcdc_dc *dc = dev->dev_private; 510 struct atmel_hlcdc_dc_commit *commit; 511 int ret; 512 513 ret = drm_atomic_helper_prepare_planes(dev, state); 514 if (ret) 515 return ret; 516 517 /* Allocate the commit object. */ 518 commit = kzalloc(sizeof(*commit), GFP_KERNEL); 519 if (!commit) { 520 ret = -ENOMEM; 521 goto error; 522 } 523 524 INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work); 525 commit->dev = dev; 526 commit->state = state; 527 528 spin_lock(&dc->commit.wait.lock); 529 ret = wait_event_interruptible_locked(dc->commit.wait, 530 !dc->commit.pending); 531 if (ret == 0) 532 dc->commit.pending = true; 533 spin_unlock(&dc->commit.wait.lock); 534 535 if (ret) 536 goto err_free; 537 538 /* We have our own synchronization through the commit lock. */ 539 BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); 540 541 /* Swap state succeeded, this is the point of no return. */ 542 drm_atomic_state_get(state); 543 if (async) 544 queue_work(dc->wq, &commit->work); 545 else 546 atmel_hlcdc_dc_atomic_complete(commit); 547 548 return 0; 549 550 err_free: 551 kfree(commit); 552 error: 553 drm_atomic_helper_cleanup_planes(dev, state); 554 return ret; 555 } 556 557 static const struct drm_mode_config_funcs mode_config_funcs = { 558 .fb_create = atmel_hlcdc_fb_create, 559 .atomic_check = drm_atomic_helper_check, 560 .atomic_commit = atmel_hlcdc_dc_atomic_commit, 561 }; 562 563 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 564 { 565 struct atmel_hlcdc_dc *dc = dev->dev_private; 566 int ret; 567 568 drm_mode_config_init(dev); 569 570 ret = atmel_hlcdc_create_outputs(dev); 571 if (ret) { 572 dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); 573 return ret; 574 } 575 576 ret = atmel_hlcdc_create_planes(dev); 577 if (ret) { 578 dev_err(dev->dev, "failed to create planes: %d\n", ret); 579 return ret; 580 } 581 582 ret = atmel_hlcdc_crtc_create(dev); 583 if (ret) { 584 dev_err(dev->dev, "failed to create crtc\n"); 585 return ret; 586 } 587 588 dev->mode_config.min_width = dc->desc->min_width; 589 dev->mode_config.min_height = dc->desc->min_height; 590 dev->mode_config.max_width = dc->desc->max_width; 591 dev->mode_config.max_height = dc->desc->max_height; 592 dev->mode_config.funcs = &mode_config_funcs; 593 594 return 0; 595 } 596 597 static int atmel_hlcdc_dc_load(struct drm_device *dev) 598 { 599 struct platform_device *pdev = to_platform_device(dev->dev); 600 const struct of_device_id *match; 601 struct atmel_hlcdc_dc *dc; 602 int ret; 603 604 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 605 if (!match) { 606 dev_err(&pdev->dev, "invalid compatible string\n"); 607 return -ENODEV; 608 } 609 610 if (!match->data) { 611 dev_err(&pdev->dev, "invalid hlcdc description\n"); 612 return -EINVAL; 613 } 614 615 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 616 if (!dc) 617 return -ENOMEM; 618 619 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 620 if (!dc->wq) 621 return -ENOMEM; 622 623 init_waitqueue_head(&dc->commit.wait); 624 dc->desc = match->data; 625 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 626 dev->dev_private = dc; 627 628 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 629 if (ret) { 630 dev_err(dev->dev, "failed to enable periph_clk\n"); 631 goto err_destroy_wq; 632 } 633 634 pm_runtime_enable(dev->dev); 635 636 ret = drm_vblank_init(dev, 1); 637 if (ret < 0) { 638 dev_err(dev->dev, "failed to initialize vblank\n"); 639 goto err_periph_clk_disable; 640 } 641 642 ret = atmel_hlcdc_dc_modeset_init(dev); 643 if (ret < 0) { 644 dev_err(dev->dev, "failed to initialize mode setting\n"); 645 goto err_periph_clk_disable; 646 } 647 648 drm_mode_config_reset(dev); 649 650 pm_runtime_get_sync(dev->dev); 651 ret = drm_irq_install(dev, dc->hlcdc->irq); 652 pm_runtime_put_sync(dev->dev); 653 if (ret < 0) { 654 dev_err(dev->dev, "failed to install IRQ handler\n"); 655 goto err_periph_clk_disable; 656 } 657 658 platform_set_drvdata(pdev, dev); 659 660 drm_kms_helper_poll_init(dev); 661 662 return 0; 663 664 err_periph_clk_disable: 665 pm_runtime_disable(dev->dev); 666 clk_disable_unprepare(dc->hlcdc->periph_clk); 667 668 err_destroy_wq: 669 destroy_workqueue(dc->wq); 670 671 return ret; 672 } 673 674 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 675 { 676 struct atmel_hlcdc_dc *dc = dev->dev_private; 677 678 flush_workqueue(dc->wq); 679 drm_kms_helper_poll_fini(dev); 680 drm_atomic_helper_shutdown(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 int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 695 { 696 struct atmel_hlcdc_dc *dc = dev->dev_private; 697 unsigned int cfg = 0; 698 int i; 699 700 /* Enable interrupts on activated layers */ 701 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 702 if (dc->layers[i]) 703 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 704 } 705 706 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 707 708 return 0; 709 } 710 711 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 712 { 713 struct atmel_hlcdc_dc *dc = dev->dev_private; 714 unsigned int isr; 715 716 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 717 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 718 } 719 720 DEFINE_DRM_GEM_CMA_FOPS(fops); 721 722 static struct drm_driver atmel_hlcdc_dc_driver = { 723 .driver_features = DRIVER_GEM | 724 DRIVER_MODESET | DRIVER_PRIME | 725 DRIVER_ATOMIC, 726 .irq_handler = atmel_hlcdc_dc_irq_handler, 727 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 728 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 729 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 730 .gem_free_object_unlocked = drm_gem_cma_free_object, 731 .gem_vm_ops = &drm_gem_cma_vm_ops, 732 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 733 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 734 .gem_prime_import = drm_gem_prime_import, 735 .gem_prime_export = drm_gem_prime_export, 736 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 737 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 738 .gem_prime_vmap = drm_gem_cma_prime_vmap, 739 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 740 .gem_prime_mmap = drm_gem_cma_prime_mmap, 741 .dumb_create = drm_gem_cma_dumb_create, 742 .fops = &fops, 743 .name = "atmel-hlcdc", 744 .desc = "Atmel HLCD Controller DRM", 745 .date = "20141504", 746 .major = 1, 747 .minor = 0, 748 }; 749 750 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 751 { 752 struct drm_device *ddev; 753 int ret; 754 755 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 756 if (IS_ERR(ddev)) 757 return PTR_ERR(ddev); 758 759 ret = atmel_hlcdc_dc_load(ddev); 760 if (ret) 761 goto err_put; 762 763 ret = drm_dev_register(ddev, 0); 764 if (ret) 765 goto err_unload; 766 767 drm_fbdev_generic_setup(ddev, 24); 768 769 return 0; 770 771 err_unload: 772 atmel_hlcdc_dc_unload(ddev); 773 774 err_put: 775 drm_dev_put(ddev); 776 777 return ret; 778 } 779 780 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 781 { 782 struct drm_device *ddev = platform_get_drvdata(pdev); 783 784 drm_dev_unregister(ddev); 785 atmel_hlcdc_dc_unload(ddev); 786 drm_dev_put(ddev); 787 788 return 0; 789 } 790 791 #ifdef CONFIG_PM_SLEEP 792 static int atmel_hlcdc_dc_drm_suspend(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 struct regmap *regmap = dc->hlcdc->regmap; 797 struct drm_atomic_state *state; 798 799 state = drm_atomic_helper_suspend(drm_dev); 800 if (IS_ERR(state)) 801 return PTR_ERR(state); 802 803 dc->suspend.state = state; 804 805 regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); 806 regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); 807 clk_disable_unprepare(dc->hlcdc->periph_clk); 808 809 return 0; 810 } 811 812 static int atmel_hlcdc_dc_drm_resume(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 817 clk_prepare_enable(dc->hlcdc->periph_clk); 818 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); 819 820 return drm_atomic_helper_resume(drm_dev, dc->suspend.state); 821 } 822 #endif 823 824 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 825 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 826 827 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 828 { .compatible = "atmel,hlcdc-display-controller" }, 829 { }, 830 }; 831 832 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 833 .probe = atmel_hlcdc_dc_drm_probe, 834 .remove = atmel_hlcdc_dc_drm_remove, 835 .driver = { 836 .name = "atmel-hlcdc-display-controller", 837 .pm = &atmel_hlcdc_dc_drm_pm_ops, 838 .of_match_table = atmel_hlcdc_dc_of_match, 839 }, 840 }; 841 module_platform_driver(atmel_hlcdc_dc_platform_driver); 842 843 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 844 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 845 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 846 MODULE_LICENSE("GPL"); 847 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 848