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