cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");