Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014 Traphandler
0004  * Copyright (C) 2014 Free Electrons
0005  * Copyright (C) 2014 Atmel
0006  *
0007  * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
0008  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
0009  */
0010 
0011 #include <linux/clk.h>
0012 #include <linux/irq.h>
0013 #include <linux/irqchip.h>
0014 #include <linux/mfd/atmel-hlcdc.h>
0015 #include <linux/module.h>
0016 #include <linux/pm_runtime.h>
0017 #include <linux/platform_device.h>
0018 
0019 #include <drm/drm_atomic.h>
0020 #include <drm/drm_atomic_helper.h>
0021 #include <drm/drm_drv.h>
0022 #include <drm/drm_fb_helper.h>
0023 #include <drm/drm_gem_cma_helper.h>
0024 #include <drm/drm_gem_framebuffer_helper.h>
0025 #include <drm/drm_module.h>
0026 #include <drm/drm_probe_helper.h>
0027 #include <drm/drm_vblank.h>
0028 
0029 #include "atmel_hlcdc_dc.h"
0030 
0031 #define ATMEL_HLCDC_LAYER_IRQS_OFFSET       8
0032 
0033 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
0034     {
0035         .name = "base",
0036         .formats = &atmel_hlcdc_plane_rgb_formats,
0037         .regs_offset = 0x40,
0038         .id = 0,
0039         .type = ATMEL_HLCDC_BASE_LAYER,
0040         .cfgs_offset = 0x2c,
0041         .layout = {
0042             .xstride = { 2 },
0043             .default_color = 3,
0044             .general_config = 4,
0045         },
0046         .clut_offset = 0x400,
0047     },
0048 };
0049 
0050 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = {
0051     .min_width = 0,
0052     .min_height = 0,
0053     .max_width = 1280,
0054     .max_height = 860,
0055     .max_spw = 0x3f,
0056     .max_vpw = 0x3f,
0057     .max_hpw = 0xff,
0058     .conflicting_output_formats = true,
0059     .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers),
0060     .layers = atmel_hlcdc_at91sam9n12_layers,
0061 };
0062 
0063 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
0064     {
0065         .name = "base",
0066         .formats = &atmel_hlcdc_plane_rgb_formats,
0067         .regs_offset = 0x40,
0068         .id = 0,
0069         .type = ATMEL_HLCDC_BASE_LAYER,
0070         .cfgs_offset = 0x2c,
0071         .layout = {
0072             .xstride = { 2 },
0073             .default_color = 3,
0074             .general_config = 4,
0075             .disc_pos = 5,
0076             .disc_size = 6,
0077         },
0078         .clut_offset = 0x400,
0079     },
0080     {
0081         .name = "overlay1",
0082         .formats = &atmel_hlcdc_plane_rgb_formats,
0083         .regs_offset = 0x100,
0084         .id = 1,
0085         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0086         .cfgs_offset = 0x2c,
0087         .layout = {
0088             .pos = 2,
0089             .size = 3,
0090             .xstride = { 4 },
0091             .pstride = { 5 },
0092             .default_color = 6,
0093             .chroma_key = 7,
0094             .chroma_key_mask = 8,
0095             .general_config = 9,
0096         },
0097         .clut_offset = 0x800,
0098     },
0099     {
0100         .name = "high-end-overlay",
0101         .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
0102         .regs_offset = 0x280,
0103         .id = 2,
0104         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0105         .cfgs_offset = 0x4c,
0106         .layout = {
0107             .pos = 2,
0108             .size = 3,
0109             .memsize = 4,
0110             .xstride = { 5, 7 },
0111             .pstride = { 6, 8 },
0112             .default_color = 9,
0113             .chroma_key = 10,
0114             .chroma_key_mask = 11,
0115             .general_config = 12,
0116             .scaler_config = 13,
0117             .csc = 14,
0118         },
0119         .clut_offset = 0x1000,
0120     },
0121     {
0122         .name = "cursor",
0123         .formats = &atmel_hlcdc_plane_rgb_formats,
0124         .regs_offset = 0x340,
0125         .id = 3,
0126         .type = ATMEL_HLCDC_CURSOR_LAYER,
0127         .max_width = 128,
0128         .max_height = 128,
0129         .cfgs_offset = 0x2c,
0130         .layout = {
0131             .pos = 2,
0132             .size = 3,
0133             .xstride = { 4 },
0134             .default_color = 6,
0135             .chroma_key = 7,
0136             .chroma_key_mask = 8,
0137             .general_config = 9,
0138         },
0139         .clut_offset = 0x1400,
0140     },
0141 };
0142 
0143 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = {
0144     .min_width = 0,
0145     .min_height = 0,
0146     .max_width = 800,
0147     .max_height = 600,
0148     .max_spw = 0x3f,
0149     .max_vpw = 0x3f,
0150     .max_hpw = 0xff,
0151     .conflicting_output_formats = true,
0152     .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers),
0153     .layers = atmel_hlcdc_at91sam9x5_layers,
0154 };
0155 
0156 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
0157     {
0158         .name = "base",
0159         .formats = &atmel_hlcdc_plane_rgb_formats,
0160         .regs_offset = 0x40,
0161         .id = 0,
0162         .type = ATMEL_HLCDC_BASE_LAYER,
0163         .cfgs_offset = 0x2c,
0164         .layout = {
0165             .xstride = { 2 },
0166             .default_color = 3,
0167             .general_config = 4,
0168             .disc_pos = 5,
0169             .disc_size = 6,
0170         },
0171         .clut_offset = 0x600,
0172     },
0173     {
0174         .name = "overlay1",
0175         .formats = &atmel_hlcdc_plane_rgb_formats,
0176         .regs_offset = 0x140,
0177         .id = 1,
0178         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0179         .cfgs_offset = 0x2c,
0180         .layout = {
0181             .pos = 2,
0182             .size = 3,
0183             .xstride = { 4 },
0184             .pstride = { 5 },
0185             .default_color = 6,
0186             .chroma_key = 7,
0187             .chroma_key_mask = 8,
0188             .general_config = 9,
0189         },
0190         .clut_offset = 0xa00,
0191     },
0192     {
0193         .name = "overlay2",
0194         .formats = &atmel_hlcdc_plane_rgb_formats,
0195         .regs_offset = 0x240,
0196         .id = 2,
0197         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0198         .cfgs_offset = 0x2c,
0199         .layout = {
0200             .pos = 2,
0201             .size = 3,
0202             .xstride = { 4 },
0203             .pstride = { 5 },
0204             .default_color = 6,
0205             .chroma_key = 7,
0206             .chroma_key_mask = 8,
0207             .general_config = 9,
0208         },
0209         .clut_offset = 0xe00,
0210     },
0211     {
0212         .name = "high-end-overlay",
0213         .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
0214         .regs_offset = 0x340,
0215         .id = 3,
0216         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0217         .cfgs_offset = 0x4c,
0218         .layout = {
0219             .pos = 2,
0220             .size = 3,
0221             .memsize = 4,
0222             .xstride = { 5, 7 },
0223             .pstride = { 6, 8 },
0224             .default_color = 9,
0225             .chroma_key = 10,
0226             .chroma_key_mask = 11,
0227             .general_config = 12,
0228             .scaler_config = 13,
0229             .phicoeffs = {
0230                 .x = 17,
0231                 .y = 33,
0232             },
0233             .csc = 14,
0234         },
0235         .clut_offset = 0x1200,
0236     },
0237     {
0238         .name = "cursor",
0239         .formats = &atmel_hlcdc_plane_rgb_formats,
0240         .regs_offset = 0x440,
0241         .id = 4,
0242         .type = ATMEL_HLCDC_CURSOR_LAYER,
0243         .max_width = 128,
0244         .max_height = 128,
0245         .cfgs_offset = 0x2c,
0246         .layout = {
0247             .pos = 2,
0248             .size = 3,
0249             .xstride = { 4 },
0250             .pstride = { 5 },
0251             .default_color = 6,
0252             .chroma_key = 7,
0253             .chroma_key_mask = 8,
0254             .general_config = 9,
0255             .scaler_config = 13,
0256         },
0257         .clut_offset = 0x1600,
0258     },
0259 };
0260 
0261 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
0262     .min_width = 0,
0263     .min_height = 0,
0264     .max_width = 2048,
0265     .max_height = 2048,
0266     .max_spw = 0x3f,
0267     .max_vpw = 0x3f,
0268     .max_hpw = 0x1ff,
0269     .conflicting_output_formats = true,
0270     .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
0271     .layers = atmel_hlcdc_sama5d3_layers,
0272 };
0273 
0274 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
0275     {
0276         .name = "base",
0277         .formats = &atmel_hlcdc_plane_rgb_formats,
0278         .regs_offset = 0x40,
0279         .id = 0,
0280         .type = ATMEL_HLCDC_BASE_LAYER,
0281         .cfgs_offset = 0x2c,
0282         .layout = {
0283             .xstride = { 2 },
0284             .default_color = 3,
0285             .general_config = 4,
0286             .disc_pos = 5,
0287             .disc_size = 6,
0288         },
0289         .clut_offset = 0x600,
0290     },
0291     {
0292         .name = "overlay1",
0293         .formats = &atmel_hlcdc_plane_rgb_formats,
0294         .regs_offset = 0x140,
0295         .id = 1,
0296         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0297         .cfgs_offset = 0x2c,
0298         .layout = {
0299             .pos = 2,
0300             .size = 3,
0301             .xstride = { 4 },
0302             .pstride = { 5 },
0303             .default_color = 6,
0304             .chroma_key = 7,
0305             .chroma_key_mask = 8,
0306             .general_config = 9,
0307         },
0308         .clut_offset = 0xa00,
0309     },
0310     {
0311         .name = "overlay2",
0312         .formats = &atmel_hlcdc_plane_rgb_formats,
0313         .regs_offset = 0x240,
0314         .id = 2,
0315         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0316         .cfgs_offset = 0x2c,
0317         .layout = {
0318             .pos = 2,
0319             .size = 3,
0320             .xstride = { 4 },
0321             .pstride = { 5 },
0322             .default_color = 6,
0323             .chroma_key = 7,
0324             .chroma_key_mask = 8,
0325             .general_config = 9,
0326         },
0327         .clut_offset = 0xe00,
0328     },
0329     {
0330         .name = "high-end-overlay",
0331         .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
0332         .regs_offset = 0x340,
0333         .id = 3,
0334         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0335         .cfgs_offset = 0x4c,
0336         .layout = {
0337             .pos = 2,
0338             .size = 3,
0339             .memsize = 4,
0340             .xstride = { 5, 7 },
0341             .pstride = { 6, 8 },
0342             .default_color = 9,
0343             .chroma_key = 10,
0344             .chroma_key_mask = 11,
0345             .general_config = 12,
0346             .scaler_config = 13,
0347             .phicoeffs = {
0348                 .x = 17,
0349                 .y = 33,
0350             },
0351             .csc = 14,
0352         },
0353         .clut_offset = 0x1200,
0354     },
0355 };
0356 
0357 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = {
0358     .min_width = 0,
0359     .min_height = 0,
0360     .max_width = 2048,
0361     .max_height = 2048,
0362     .max_spw = 0xff,
0363     .max_vpw = 0xff,
0364     .max_hpw = 0x3ff,
0365     .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers),
0366     .layers = atmel_hlcdc_sama5d4_layers,
0367 };
0368 
0369 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sam9x60_layers[] = {
0370     {
0371         .name = "base",
0372         .formats = &atmel_hlcdc_plane_rgb_formats,
0373         .regs_offset = 0x60,
0374         .id = 0,
0375         .type = ATMEL_HLCDC_BASE_LAYER,
0376         .cfgs_offset = 0x2c,
0377         .layout = {
0378             .xstride = { 2 },
0379             .default_color = 3,
0380             .general_config = 4,
0381             .disc_pos = 5,
0382             .disc_size = 6,
0383         },
0384         .clut_offset = 0x600,
0385     },
0386     {
0387         .name = "overlay1",
0388         .formats = &atmel_hlcdc_plane_rgb_formats,
0389         .regs_offset = 0x160,
0390         .id = 1,
0391         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0392         .cfgs_offset = 0x2c,
0393         .layout = {
0394             .pos = 2,
0395             .size = 3,
0396             .xstride = { 4 },
0397             .pstride = { 5 },
0398             .default_color = 6,
0399             .chroma_key = 7,
0400             .chroma_key_mask = 8,
0401             .general_config = 9,
0402         },
0403         .clut_offset = 0xa00,
0404     },
0405     {
0406         .name = "overlay2",
0407         .formats = &atmel_hlcdc_plane_rgb_formats,
0408         .regs_offset = 0x260,
0409         .id = 2,
0410         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0411         .cfgs_offset = 0x2c,
0412         .layout = {
0413             .pos = 2,
0414             .size = 3,
0415             .xstride = { 4 },
0416             .pstride = { 5 },
0417             .default_color = 6,
0418             .chroma_key = 7,
0419             .chroma_key_mask = 8,
0420             .general_config = 9,
0421         },
0422         .clut_offset = 0xe00,
0423     },
0424     {
0425         .name = "high-end-overlay",
0426         .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
0427         .regs_offset = 0x360,
0428         .id = 3,
0429         .type = ATMEL_HLCDC_OVERLAY_LAYER,
0430         .cfgs_offset = 0x4c,
0431         .layout = {
0432             .pos = 2,
0433             .size = 3,
0434             .memsize = 4,
0435             .xstride = { 5, 7 },
0436             .pstride = { 6, 8 },
0437             .default_color = 9,
0438             .chroma_key = 10,
0439             .chroma_key_mask = 11,
0440             .general_config = 12,
0441             .scaler_config = 13,
0442             .phicoeffs = {
0443                 .x = 17,
0444                 .y = 33,
0445             },
0446             .csc = 14,
0447         },
0448         .clut_offset = 0x1200,
0449     },
0450 };
0451 
0452 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sam9x60 = {
0453     .min_width = 0,
0454     .min_height = 0,
0455     .max_width = 2048,
0456     .max_height = 2048,
0457     .max_spw = 0xff,
0458     .max_vpw = 0xff,
0459     .max_hpw = 0x3ff,
0460     .fixed_clksrc = true,
0461     .nlayers = ARRAY_SIZE(atmel_hlcdc_sam9x60_layers),
0462     .layers = atmel_hlcdc_sam9x60_layers,
0463 };
0464 
0465 static const struct of_device_id atmel_hlcdc_of_match[] = {
0466     {
0467         .compatible = "atmel,at91sam9n12-hlcdc",
0468         .data = &atmel_hlcdc_dc_at91sam9n12,
0469     },
0470     {
0471         .compatible = "atmel,at91sam9x5-hlcdc",
0472         .data = &atmel_hlcdc_dc_at91sam9x5,
0473     },
0474     {
0475         .compatible = "atmel,sama5d2-hlcdc",
0476         .data = &atmel_hlcdc_dc_sama5d4,
0477     },
0478     {
0479         .compatible = "atmel,sama5d3-hlcdc",
0480         .data = &atmel_hlcdc_dc_sama5d3,
0481     },
0482     {
0483         .compatible = "atmel,sama5d4-hlcdc",
0484         .data = &atmel_hlcdc_dc_sama5d4,
0485     },
0486     {
0487         .compatible = "microchip,sam9x60-hlcdc",
0488         .data = &atmel_hlcdc_dc_sam9x60,
0489     },
0490     { /* sentinel */ },
0491 };
0492 MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match);
0493 
0494 enum drm_mode_status
0495 atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
0496               const struct drm_display_mode *mode)
0497 {
0498     int vfront_porch = mode->vsync_start - mode->vdisplay;
0499     int vback_porch = mode->vtotal - mode->vsync_end;
0500     int vsync_len = mode->vsync_end - mode->vsync_start;
0501     int hfront_porch = mode->hsync_start - mode->hdisplay;
0502     int hback_porch = mode->htotal - mode->hsync_end;
0503     int hsync_len = mode->hsync_end - mode->hsync_start;
0504 
0505     if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1)
0506         return MODE_HSYNC;
0507 
0508     if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1)
0509         return MODE_VSYNC;
0510 
0511     if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 ||
0512         hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 ||
0513         mode->hdisplay < 1)
0514         return MODE_H_ILLEGAL;
0515 
0516     if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 ||
0517         vback_porch > dc->desc->max_vpw || vback_porch < 0 ||
0518         mode->vdisplay < 1)
0519         return MODE_V_ILLEGAL;
0520 
0521     return MODE_OK;
0522 }
0523 
0524 static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
0525 {
0526     if (!layer)
0527         return;
0528 
0529     if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER ||
0530         layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
0531         layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER)
0532         atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer));
0533 }
0534 
0535 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
0536 {
0537     struct drm_device *dev = data;
0538     struct atmel_hlcdc_dc *dc = dev->dev_private;
0539     unsigned long status;
0540     unsigned int imr, isr;
0541     int i;
0542 
0543     regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
0544     regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
0545     status = imr & isr;
0546     if (!status)
0547         return IRQ_NONE;
0548 
0549     if (status & ATMEL_HLCDC_SOF)
0550         atmel_hlcdc_crtc_irq(dc->crtc);
0551 
0552     for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
0553         if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
0554             atmel_hlcdc_layer_irq(dc->layers[i]);
0555     }
0556 
0557     return IRQ_HANDLED;
0558 }
0559 
0560 static void atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
0561 {
0562     struct atmel_hlcdc_dc *dc = dev->dev_private;
0563     unsigned int cfg = 0;
0564     int i;
0565 
0566     /* Enable interrupts on activated layers */
0567     for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
0568         if (dc->layers[i])
0569             cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
0570     }
0571 
0572     regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg);
0573 }
0574 
0575 static void atmel_hlcdc_dc_irq_disable(struct drm_device *dev)
0576 {
0577     struct atmel_hlcdc_dc *dc = dev->dev_private;
0578     unsigned int isr;
0579 
0580     regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
0581     regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
0582 }
0583 
0584 static int atmel_hlcdc_dc_irq_install(struct drm_device *dev, unsigned int irq)
0585 {
0586     int ret;
0587 
0588     atmel_hlcdc_dc_irq_disable(dev);
0589 
0590     ret = devm_request_irq(dev->dev, irq, atmel_hlcdc_dc_irq_handler, 0,
0591                    dev->driver->name, dev);
0592     if (ret)
0593         return ret;
0594 
0595     atmel_hlcdc_dc_irq_postinstall(dev);
0596 
0597     return 0;
0598 }
0599 
0600 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
0601 {
0602     atmel_hlcdc_dc_irq_disable(dev);
0603 }
0604 
0605 static const struct drm_mode_config_funcs mode_config_funcs = {
0606     .fb_create = drm_gem_fb_create,
0607     .atomic_check = drm_atomic_helper_check,
0608     .atomic_commit = drm_atomic_helper_commit,
0609 };
0610 
0611 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
0612 {
0613     struct atmel_hlcdc_dc *dc = dev->dev_private;
0614     int ret;
0615 
0616     drm_mode_config_init(dev);
0617 
0618     ret = atmel_hlcdc_create_outputs(dev);
0619     if (ret) {
0620         dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
0621         return ret;
0622     }
0623 
0624     ret = atmel_hlcdc_create_planes(dev);
0625     if (ret) {
0626         dev_err(dev->dev, "failed to create planes: %d\n", ret);
0627         return ret;
0628     }
0629 
0630     ret = atmel_hlcdc_crtc_create(dev);
0631     if (ret) {
0632         dev_err(dev->dev, "failed to create crtc\n");
0633         return ret;
0634     }
0635 
0636     dev->mode_config.min_width = dc->desc->min_width;
0637     dev->mode_config.min_height = dc->desc->min_height;
0638     dev->mode_config.max_width = dc->desc->max_width;
0639     dev->mode_config.max_height = dc->desc->max_height;
0640     dev->mode_config.funcs = &mode_config_funcs;
0641     dev->mode_config.async_page_flip = true;
0642 
0643     return 0;
0644 }
0645 
0646 static int atmel_hlcdc_dc_load(struct drm_device *dev)
0647 {
0648     struct platform_device *pdev = to_platform_device(dev->dev);
0649     const struct of_device_id *match;
0650     struct atmel_hlcdc_dc *dc;
0651     int ret;
0652 
0653     match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
0654     if (!match) {
0655         dev_err(&pdev->dev, "invalid compatible string\n");
0656         return -ENODEV;
0657     }
0658 
0659     if (!match->data) {
0660         dev_err(&pdev->dev, "invalid hlcdc description\n");
0661         return -EINVAL;
0662     }
0663 
0664     dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
0665     if (!dc)
0666         return -ENOMEM;
0667 
0668     dc->desc = match->data;
0669     dc->hlcdc = dev_get_drvdata(dev->dev->parent);
0670     dev->dev_private = dc;
0671 
0672     ret = clk_prepare_enable(dc->hlcdc->periph_clk);
0673     if (ret) {
0674         dev_err(dev->dev, "failed to enable periph_clk\n");
0675         return ret;
0676     }
0677 
0678     pm_runtime_enable(dev->dev);
0679 
0680     ret = drm_vblank_init(dev, 1);
0681     if (ret < 0) {
0682         dev_err(dev->dev, "failed to initialize vblank\n");
0683         goto err_periph_clk_disable;
0684     }
0685 
0686     ret = atmel_hlcdc_dc_modeset_init(dev);
0687     if (ret < 0) {
0688         dev_err(dev->dev, "failed to initialize mode setting\n");
0689         goto err_periph_clk_disable;
0690     }
0691 
0692     drm_mode_config_reset(dev);
0693 
0694     pm_runtime_get_sync(dev->dev);
0695     ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq);
0696     pm_runtime_put_sync(dev->dev);
0697     if (ret < 0) {
0698         dev_err(dev->dev, "failed to install IRQ handler\n");
0699         goto err_periph_clk_disable;
0700     }
0701 
0702     platform_set_drvdata(pdev, dev);
0703 
0704     drm_kms_helper_poll_init(dev);
0705 
0706     return 0;
0707 
0708 err_periph_clk_disable:
0709     pm_runtime_disable(dev->dev);
0710     clk_disable_unprepare(dc->hlcdc->periph_clk);
0711 
0712     return ret;
0713 }
0714 
0715 static void atmel_hlcdc_dc_unload(struct drm_device *dev)
0716 {
0717     struct atmel_hlcdc_dc *dc = dev->dev_private;
0718 
0719     drm_kms_helper_poll_fini(dev);
0720     drm_atomic_helper_shutdown(dev);
0721     drm_mode_config_cleanup(dev);
0722 
0723     pm_runtime_get_sync(dev->dev);
0724     atmel_hlcdc_dc_irq_uninstall(dev);
0725     pm_runtime_put_sync(dev->dev);
0726 
0727     dev->dev_private = NULL;
0728 
0729     pm_runtime_disable(dev->dev);
0730     clk_disable_unprepare(dc->hlcdc->periph_clk);
0731 }
0732 
0733 DEFINE_DRM_GEM_CMA_FOPS(fops);
0734 
0735 static const struct drm_driver atmel_hlcdc_dc_driver = {
0736     .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
0737     DRM_GEM_CMA_DRIVER_OPS,
0738     .fops = &fops,
0739     .name = "atmel-hlcdc",
0740     .desc = "Atmel HLCD Controller DRM",
0741     .date = "20141504",
0742     .major = 1,
0743     .minor = 0,
0744 };
0745 
0746 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
0747 {
0748     struct drm_device *ddev;
0749     int ret;
0750 
0751     ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
0752     if (IS_ERR(ddev))
0753         return PTR_ERR(ddev);
0754 
0755     ret = atmel_hlcdc_dc_load(ddev);
0756     if (ret)
0757         goto err_put;
0758 
0759     ret = drm_dev_register(ddev, 0);
0760     if (ret)
0761         goto err_unload;
0762 
0763     drm_fbdev_generic_setup(ddev, 24);
0764 
0765     return 0;
0766 
0767 err_unload:
0768     atmel_hlcdc_dc_unload(ddev);
0769 
0770 err_put:
0771     drm_dev_put(ddev);
0772 
0773     return ret;
0774 }
0775 
0776 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
0777 {
0778     struct drm_device *ddev = platform_get_drvdata(pdev);
0779 
0780     drm_dev_unregister(ddev);
0781     atmel_hlcdc_dc_unload(ddev);
0782     drm_dev_put(ddev);
0783 
0784     return 0;
0785 }
0786 
0787 #ifdef CONFIG_PM_SLEEP
0788 static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
0789 {
0790     struct drm_device *drm_dev = dev_get_drvdata(dev);
0791     struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
0792     struct regmap *regmap = dc->hlcdc->regmap;
0793     struct drm_atomic_state *state;
0794 
0795     state = drm_atomic_helper_suspend(drm_dev);
0796     if (IS_ERR(state))
0797         return PTR_ERR(state);
0798 
0799     dc->suspend.state = state;
0800 
0801     regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr);
0802     regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr);
0803     clk_disable_unprepare(dc->hlcdc->periph_clk);
0804 
0805     return 0;
0806 }
0807 
0808 static int atmel_hlcdc_dc_drm_resume(struct device *dev)
0809 {
0810     struct drm_device *drm_dev = dev_get_drvdata(dev);
0811     struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
0812 
0813     clk_prepare_enable(dc->hlcdc->periph_clk);
0814     regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr);
0815 
0816     return drm_atomic_helper_resume(drm_dev, dc->suspend.state);
0817 }
0818 #endif
0819 
0820 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
0821         atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume);
0822 
0823 static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
0824     { .compatible = "atmel,hlcdc-display-controller" },
0825     { },
0826 };
0827 
0828 static struct platform_driver atmel_hlcdc_dc_platform_driver = {
0829     .probe  = atmel_hlcdc_dc_drm_probe,
0830     .remove = atmel_hlcdc_dc_drm_remove,
0831     .driver = {
0832         .name   = "atmel-hlcdc-display-controller",
0833         .pm = &atmel_hlcdc_dc_drm_pm_ops,
0834         .of_match_table = atmel_hlcdc_dc_of_match,
0835     },
0836 };
0837 drm_module_platform_driver(atmel_hlcdc_dc_platform_driver);
0838 
0839 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
0840 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
0841 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
0842 MODULE_LICENSE("GPL");
0843 MODULE_ALIAS("platform:atmel-hlcdc-dc");