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