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