blob: eb6195c108f39d486f5c66e9c1d1c72ebcaa9029 [file] [log] [blame]
Boris Brezillon1a396782015-01-06 11:13:28 +01001/*
2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
5 *
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <linux/clk.h>
23#include <linux/irq.h>
24#include <linux/irqchip.h>
25#include <linux/module.h>
26#include <linux/pm_runtime.h>
27
28#include "atmel_hlcdc_dc.h"
29
30#define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8
31
Boris Brezillon6b22cad2015-01-07 10:12:41 +010032static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
33 {
34 .name = "base",
35 .formats = &atmel_hlcdc_plane_rgb_formats,
36 .regs_offset = 0x40,
37 .id = 0,
38 .type = ATMEL_HLCDC_BASE_LAYER,
39 .nconfigs = 5,
40 .layout = {
41 .xstride = { 2 },
42 .default_color = 3,
43 .general_config = 4,
44 },
45 },
46};
47
48static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = {
49 .min_width = 0,
50 .min_height = 0,
51 .max_width = 1280,
52 .max_height = 860,
Boris Brezillon79a3fc22016-01-05 18:11:39 +010053 .max_spw = 0x3f,
54 .max_vpw = 0x3f,
55 .max_hpw = 0xff,
Boris Brezillon6b22cad2015-01-07 10:12:41 +010056 .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers),
57 .layers = atmel_hlcdc_at91sam9n12_layers,
58};
59
Boris Brezillon348ef852015-01-07 09:30:20 +010060static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
61 {
62 .name = "base",
63 .formats = &atmel_hlcdc_plane_rgb_formats,
64 .regs_offset = 0x40,
65 .id = 0,
66 .type = ATMEL_HLCDC_BASE_LAYER,
67 .nconfigs = 5,
68 .layout = {
69 .xstride = { 2 },
70 .default_color = 3,
71 .general_config = 4,
72 .disc_pos = 5,
73 .disc_size = 6,
74 },
75 },
76 {
77 .name = "overlay1",
78 .formats = &atmel_hlcdc_plane_rgb_formats,
79 .regs_offset = 0x100,
80 .id = 1,
81 .type = ATMEL_HLCDC_OVERLAY_LAYER,
82 .nconfigs = 10,
83 .layout = {
84 .pos = 2,
85 .size = 3,
86 .xstride = { 4 },
87 .pstride = { 5 },
88 .default_color = 6,
89 .chroma_key = 7,
90 .chroma_key_mask = 8,
91 .general_config = 9,
92 },
93 },
94 {
95 .name = "high-end-overlay",
96 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
97 .regs_offset = 0x280,
98 .id = 2,
99 .type = ATMEL_HLCDC_OVERLAY_LAYER,
100 .nconfigs = 17,
101 .layout = {
102 .pos = 2,
103 .size = 3,
104 .memsize = 4,
105 .xstride = { 5, 7 },
106 .pstride = { 6, 8 },
107 .default_color = 9,
108 .chroma_key = 10,
109 .chroma_key_mask = 11,
110 .general_config = 12,
111 .csc = 14,
112 },
113 },
114 {
115 .name = "cursor",
116 .formats = &atmel_hlcdc_plane_rgb_formats,
117 .regs_offset = 0x340,
118 .id = 3,
119 .type = ATMEL_HLCDC_CURSOR_LAYER,
120 .nconfigs = 10,
121 .max_width = 128,
122 .max_height = 128,
123 .layout = {
124 .pos = 2,
125 .size = 3,
126 .xstride = { 4 },
127 .default_color = 6,
128 .chroma_key = 7,
129 .chroma_key_mask = 8,
130 .general_config = 9,
131 },
132 },
133};
134
135static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = {
136 .min_width = 0,
137 .min_height = 0,
138 .max_width = 800,
139 .max_height = 600,
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100140 .max_spw = 0x3f,
141 .max_vpw = 0x3f,
142 .max_hpw = 0xff,
Boris Brezillon348ef852015-01-07 09:30:20 +0100143 .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers),
144 .layers = atmel_hlcdc_at91sam9x5_layers,
145};
146
Boris Brezillon1a396782015-01-06 11:13:28 +0100147static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
148 {
149 .name = "base",
150 .formats = &atmel_hlcdc_plane_rgb_formats,
151 .regs_offset = 0x40,
152 .id = 0,
153 .type = ATMEL_HLCDC_BASE_LAYER,
154 .nconfigs = 7,
155 .layout = {
156 .xstride = { 2 },
157 .default_color = 3,
158 .general_config = 4,
159 .disc_pos = 5,
160 .disc_size = 6,
161 },
162 },
163 {
164 .name = "overlay1",
165 .formats = &atmel_hlcdc_plane_rgb_formats,
166 .regs_offset = 0x140,
167 .id = 1,
168 .type = ATMEL_HLCDC_OVERLAY_LAYER,
169 .nconfigs = 10,
170 .layout = {
171 .pos = 2,
172 .size = 3,
173 .xstride = { 4 },
174 .pstride = { 5 },
175 .default_color = 6,
176 .chroma_key = 7,
177 .chroma_key_mask = 8,
178 .general_config = 9,
179 },
180 },
181 {
182 .name = "overlay2",
183 .formats = &atmel_hlcdc_plane_rgb_formats,
184 .regs_offset = 0x240,
185 .id = 2,
186 .type = ATMEL_HLCDC_OVERLAY_LAYER,
187 .nconfigs = 10,
188 .layout = {
189 .pos = 2,
190 .size = 3,
191 .xstride = { 4 },
192 .pstride = { 5 },
193 .default_color = 6,
194 .chroma_key = 7,
195 .chroma_key_mask = 8,
196 .general_config = 9,
197 },
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,
205 .nconfigs = 42,
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,
216 .csc = 14,
217 },
218 },
219 {
220 .name = "cursor",
221 .formats = &atmel_hlcdc_plane_rgb_formats,
222 .regs_offset = 0x440,
223 .id = 4,
224 .type = ATMEL_HLCDC_CURSOR_LAYER,
225 .nconfigs = 10,
226 .max_width = 128,
227 .max_height = 128,
228 .layout = {
229 .pos = 2,
230 .size = 3,
231 .xstride = { 4 },
232 .pstride = { 5 },
233 .default_color = 6,
234 .chroma_key = 7,
235 .chroma_key_mask = 8,
236 .general_config = 9,
237 },
238 },
239};
240
241static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
242 .min_width = 0,
243 .min_height = 0,
244 .max_width = 2048,
245 .max_height = 2048,
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100246 .max_spw = 0x3f,
247 .max_vpw = 0x3f,
248 .max_hpw = 0x1ff,
Boris Brezillon1a396782015-01-06 11:13:28 +0100249 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
250 .layers = atmel_hlcdc_sama5d3_layers,
251};
252
Boris Brezillon5b9fb5e2015-01-07 10:25:41 +0100253static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
254 {
255 .name = "base",
256 .formats = &atmel_hlcdc_plane_rgb_formats,
257 .regs_offset = 0x40,
258 .id = 0,
259 .type = ATMEL_HLCDC_BASE_LAYER,
260 .nconfigs = 7,
261 .layout = {
262 .xstride = { 2 },
263 .default_color = 3,
264 .general_config = 4,
265 .disc_pos = 5,
266 .disc_size = 6,
267 },
268 },
269 {
270 .name = "overlay1",
271 .formats = &atmel_hlcdc_plane_rgb_formats,
272 .regs_offset = 0x140,
273 .id = 1,
274 .type = ATMEL_HLCDC_OVERLAY_LAYER,
275 .nconfigs = 10,
276 .layout = {
277 .pos = 2,
278 .size = 3,
279 .xstride = { 4 },
280 .pstride = { 5 },
281 .default_color = 6,
282 .chroma_key = 7,
283 .chroma_key_mask = 8,
284 .general_config = 9,
285 },
286 },
287 {
288 .name = "overlay2",
289 .formats = &atmel_hlcdc_plane_rgb_formats,
290 .regs_offset = 0x240,
291 .id = 2,
292 .type = ATMEL_HLCDC_OVERLAY_LAYER,
293 .nconfigs = 10,
294 .layout = {
295 .pos = 2,
296 .size = 3,
297 .xstride = { 4 },
298 .pstride = { 5 },
299 .default_color = 6,
300 .chroma_key = 7,
301 .chroma_key_mask = 8,
302 .general_config = 9,
303 },
304 },
305 {
306 .name = "high-end-overlay",
307 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
308 .regs_offset = 0x340,
309 .id = 3,
310 .type = ATMEL_HLCDC_OVERLAY_LAYER,
311 .nconfigs = 42,
312 .layout = {
313 .pos = 2,
314 .size = 3,
315 .memsize = 4,
316 .xstride = { 5, 7 },
317 .pstride = { 6, 8 },
318 .default_color = 9,
319 .chroma_key = 10,
320 .chroma_key_mask = 11,
321 .general_config = 12,
322 .csc = 14,
323 },
324 },
325};
326
327static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = {
328 .min_width = 0,
329 .min_height = 0,
330 .max_width = 2048,
331 .max_height = 2048,
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100332 .max_spw = 0xff,
333 .max_vpw = 0xff,
334 .max_hpw = 0x3ff,
Boris Brezillon5b9fb5e2015-01-07 10:25:41 +0100335 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers),
336 .layers = atmel_hlcdc_sama5d4_layers,
337};
Boris Brezillon1a396782015-01-06 11:13:28 +0100338static const struct of_device_id atmel_hlcdc_of_match[] = {
339 {
Boris Brezillon6b22cad2015-01-07 10:12:41 +0100340 .compatible = "atmel,at91sam9n12-hlcdc",
341 .data = &atmel_hlcdc_dc_at91sam9n12,
342 },
343 {
Boris Brezillon348ef852015-01-07 09:30:20 +0100344 .compatible = "atmel,at91sam9x5-hlcdc",
345 .data = &atmel_hlcdc_dc_at91sam9x5,
346 },
347 {
Nicolas Ferre34649c42015-12-15 12:20:57 +0100348 .compatible = "atmel,sama5d2-hlcdc",
349 .data = &atmel_hlcdc_dc_sama5d4,
350 },
351 {
Boris Brezillon1a396782015-01-06 11:13:28 +0100352 .compatible = "atmel,sama5d3-hlcdc",
353 .data = &atmel_hlcdc_dc_sama5d3,
354 },
Boris Brezillon5b9fb5e2015-01-07 10:25:41 +0100355 {
356 .compatible = "atmel,sama5d4-hlcdc",
357 .data = &atmel_hlcdc_dc_sama5d4,
358 },
Boris Brezillon1a396782015-01-06 11:13:28 +0100359 { /* sentinel */ },
360};
Luis de Bethencourtd6ec5ec2015-12-15 12:21:16 +0100361MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match);
Boris Brezillon1a396782015-01-06 11:13:28 +0100362
363int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
364 struct drm_display_mode *mode)
365{
366 int vfront_porch = mode->vsync_start - mode->vdisplay;
367 int vback_porch = mode->vtotal - mode->vsync_end;
368 int vsync_len = mode->vsync_end - mode->vsync_start;
369 int hfront_porch = mode->hsync_start - mode->hdisplay;
370 int hback_porch = mode->htotal - mode->hsync_end;
371 int hsync_len = mode->hsync_end - mode->hsync_start;
372
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100373 if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1)
Boris Brezillon1a396782015-01-06 11:13:28 +0100374 return MODE_HSYNC;
375
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100376 if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1)
Boris Brezillon1a396782015-01-06 11:13:28 +0100377 return MODE_VSYNC;
378
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100379 if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 ||
380 hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 ||
Boris Brezillon1a396782015-01-06 11:13:28 +0100381 mode->hdisplay < 1)
382 return MODE_H_ILLEGAL;
383
Boris Brezillon79a3fc22016-01-05 18:11:39 +0100384 if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 ||
385 vback_porch > dc->desc->max_vpw || vback_porch < 0 ||
Boris Brezillon1a396782015-01-06 11:13:28 +0100386 mode->vdisplay < 1)
387 return MODE_V_ILLEGAL;
388
389 return MODE_OK;
390}
391
392static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
393{
394 struct drm_device *dev = data;
395 struct atmel_hlcdc_dc *dc = dev->dev_private;
396 unsigned long status;
397 unsigned int imr, isr;
398 int i;
399
400 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
401 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
402 status = imr & isr;
403 if (!status)
404 return IRQ_NONE;
405
406 if (status & ATMEL_HLCDC_SOF)
407 atmel_hlcdc_crtc_irq(dc->crtc);
408
409 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
410 struct atmel_hlcdc_layer *layer = dc->layers[i];
411
412 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
413 continue;
414
415 atmel_hlcdc_layer_irq(layer);
416 }
417
418 return IRQ_HANDLED;
419}
420
421static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
Ville Syrjälä1eb83452015-11-11 19:11:29 +0200422 struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
Boris Brezillon1a396782015-01-06 11:13:28 +0100423{
424 return drm_fb_cma_create(dev, file_priv, mode_cmd);
425}
426
427static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
428{
429 struct atmel_hlcdc_dc *dc = dev->dev_private;
430
431 if (dc->fbdev) {
432 drm_fbdev_cma_hotplug_event(dc->fbdev);
433 } else {
434 dc->fbdev = drm_fbdev_cma_init(dev, 24,
435 dev->mode_config.num_crtc,
436 dev->mode_config.num_connector);
437 if (IS_ERR(dc->fbdev))
438 dc->fbdev = NULL;
439 }
440}
441
Boris Brezillon9b190612015-10-10 08:22:09 +0200442struct atmel_hlcdc_dc_commit {
443 struct work_struct work;
444 struct drm_device *dev;
445 struct drm_atomic_state *state;
446};
447
448static void
449atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit)
450{
451 struct drm_device *dev = commit->dev;
452 struct atmel_hlcdc_dc *dc = dev->dev_private;
453 struct drm_atomic_state *old_state = commit->state;
454
455 /* Apply the atomic update. */
456 drm_atomic_helper_commit_modeset_disables(dev, old_state);
457 drm_atomic_helper_commit_planes(dev, old_state, false);
458 drm_atomic_helper_commit_modeset_enables(dev, old_state);
459
460 drm_atomic_helper_wait_for_vblanks(dev, old_state);
461
462 drm_atomic_helper_cleanup_planes(dev, old_state);
463
464 drm_atomic_state_free(old_state);
465
466 /* Complete the commit, wake up any waiter. */
467 spin_lock(&dc->commit.wait.lock);
468 dc->commit.pending = false;
469 wake_up_all_locked(&dc->commit.wait);
470 spin_unlock(&dc->commit.wait.lock);
471
472 kfree(commit);
473}
474
475static void atmel_hlcdc_dc_atomic_work(struct work_struct *work)
476{
477 struct atmel_hlcdc_dc_commit *commit =
478 container_of(work, struct atmel_hlcdc_dc_commit, work);
479
480 atmel_hlcdc_dc_atomic_complete(commit);
481}
482
483static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev,
484 struct drm_atomic_state *state,
485 bool async)
486{
487 struct atmel_hlcdc_dc *dc = dev->dev_private;
488 struct atmel_hlcdc_dc_commit *commit;
489 int ret;
490
491 ret = drm_atomic_helper_prepare_planes(dev, state);
492 if (ret)
493 return ret;
494
495 /* Allocate the commit object. */
496 commit = kzalloc(sizeof(*commit), GFP_KERNEL);
497 if (!commit) {
498 ret = -ENOMEM;
499 goto error;
500 }
501
502 INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work);
503 commit->dev = dev;
504 commit->state = state;
505
506 spin_lock(&dc->commit.wait.lock);
507 ret = wait_event_interruptible_locked(dc->commit.wait,
508 !dc->commit.pending);
509 if (ret == 0)
510 dc->commit.pending = true;
511 spin_unlock(&dc->commit.wait.lock);
512
513 if (ret) {
514 kfree(commit);
515 goto error;
516 }
517
518 /* Swap the state, this is the point of no return. */
519 drm_atomic_helper_swap_state(dev, state);
520
521 if (async)
522 queue_work(dc->wq, &commit->work);
523 else
524 atmel_hlcdc_dc_atomic_complete(commit);
525
526 return 0;
527
528error:
529 drm_atomic_helper_cleanup_planes(dev, state);
530 return ret;
531}
532
Boris Brezillon1a396782015-01-06 11:13:28 +0100533static const struct drm_mode_config_funcs mode_config_funcs = {
534 .fb_create = atmel_hlcdc_fb_create,
535 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
Boris Brezillon2389fc12015-02-05 16:32:33 +0100536 .atomic_check = drm_atomic_helper_check,
Boris Brezillon9b190612015-10-10 08:22:09 +0200537 .atomic_commit = atmel_hlcdc_dc_atomic_commit,
Boris Brezillon1a396782015-01-06 11:13:28 +0100538};
539
540static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
541{
542 struct atmel_hlcdc_dc *dc = dev->dev_private;
543 struct atmel_hlcdc_planes *planes;
544 int ret;
545 int i;
546
547 drm_mode_config_init(dev);
548
549 ret = atmel_hlcdc_create_outputs(dev);
550 if (ret) {
551 dev_err(dev->dev, "failed to create panel: %d\n", ret);
552 return ret;
553 }
554
555 planes = atmel_hlcdc_create_planes(dev);
556 if (IS_ERR(planes)) {
557 dev_err(dev->dev, "failed to create planes\n");
558 return PTR_ERR(planes);
559 }
560
561 dc->planes = planes;
562
563 dc->layers[planes->primary->layer.desc->id] =
564 &planes->primary->layer;
565
566 if (planes->cursor)
567 dc->layers[planes->cursor->layer.desc->id] =
568 &planes->cursor->layer;
569
570 for (i = 0; i < planes->noverlays; i++)
571 dc->layers[planes->overlays[i]->layer.desc->id] =
572 &planes->overlays[i]->layer;
573
574 ret = atmel_hlcdc_crtc_create(dev);
575 if (ret) {
576 dev_err(dev->dev, "failed to create crtc\n");
577 return ret;
578 }
579
580 dev->mode_config.min_width = dc->desc->min_width;
581 dev->mode_config.min_height = dc->desc->min_height;
582 dev->mode_config.max_width = dc->desc->max_width;
583 dev->mode_config.max_height = dc->desc->max_height;
584 dev->mode_config.funcs = &mode_config_funcs;
585
586 return 0;
587}
588
589static int atmel_hlcdc_dc_load(struct drm_device *dev)
590{
591 struct platform_device *pdev = to_platform_device(dev->dev);
592 const struct of_device_id *match;
593 struct atmel_hlcdc_dc *dc;
594 int ret;
595
596 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
597 if (!match) {
598 dev_err(&pdev->dev, "invalid compatible string\n");
599 return -ENODEV;
600 }
601
602 if (!match->data) {
603 dev_err(&pdev->dev, "invalid hlcdc description\n");
604 return -EINVAL;
605 }
606
607 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
608 if (!dc)
609 return -ENOMEM;
610
611 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
612 if (!dc->wq)
613 return -ENOMEM;
614
Boris Brezillon9b190612015-10-10 08:22:09 +0200615 init_waitqueue_head(&dc->commit.wait);
Boris Brezillon1a396782015-01-06 11:13:28 +0100616 dc->desc = match->data;
617 dc->hlcdc = dev_get_drvdata(dev->dev->parent);
618 dev->dev_private = dc;
619
620 ret = clk_prepare_enable(dc->hlcdc->periph_clk);
621 if (ret) {
622 dev_err(dev->dev, "failed to enable periph_clk\n");
623 goto err_destroy_wq;
624 }
625
626 pm_runtime_enable(dev->dev);
627
Boris Brezillon8c4b4b02015-07-16 20:55:34 +0200628 ret = drm_vblank_init(dev, 1);
629 if (ret < 0) {
630 dev_err(dev->dev, "failed to initialize vblank\n");
631 goto err_periph_clk_disable;
632 }
633
Boris Brezillon1a396782015-01-06 11:13:28 +0100634 ret = atmel_hlcdc_dc_modeset_init(dev);
635 if (ret < 0) {
636 dev_err(dev->dev, "failed to initialize mode setting\n");
637 goto err_periph_clk_disable;
638 }
639
Boris Brezillon2389fc12015-02-05 16:32:33 +0100640 drm_mode_config_reset(dev);
641
Boris Brezillon1a396782015-01-06 11:13:28 +0100642 pm_runtime_get_sync(dev->dev);
643 ret = drm_irq_install(dev, dc->hlcdc->irq);
644 pm_runtime_put_sync(dev->dev);
645 if (ret < 0) {
646 dev_err(dev->dev, "failed to install IRQ handler\n");
647 goto err_periph_clk_disable;
648 }
649
650 platform_set_drvdata(pdev, dev);
651
652 drm_kms_helper_poll_init(dev);
653
654 /* force connectors detection */
655 drm_helper_hpd_irq_event(dev);
656
657 return 0;
658
659err_periph_clk_disable:
660 pm_runtime_disable(dev->dev);
661 clk_disable_unprepare(dc->hlcdc->periph_clk);
662
663err_destroy_wq:
664 destroy_workqueue(dc->wq);
665
666 return ret;
667}
668
669static void atmel_hlcdc_dc_unload(struct drm_device *dev)
670{
671 struct atmel_hlcdc_dc *dc = dev->dev_private;
672
673 if (dc->fbdev)
674 drm_fbdev_cma_fini(dc->fbdev);
675 flush_workqueue(dc->wq);
676 drm_kms_helper_poll_fini(dev);
677 drm_mode_config_cleanup(dev);
678 drm_vblank_cleanup(dev);
679
680 pm_runtime_get_sync(dev->dev);
681 drm_irq_uninstall(dev);
682 pm_runtime_put_sync(dev->dev);
683
684 dev->dev_private = NULL;
685
686 pm_runtime_disable(dev->dev);
687 clk_disable_unprepare(dc->hlcdc->periph_clk);
688 destroy_workqueue(dc->wq);
689}
690
691static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev)
692{
693 struct drm_connector *connector, *failed;
694 int ret;
695
696 mutex_lock(&dev->mode_config.mutex);
697 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
698 ret = drm_connector_register(connector);
699 if (ret) {
700 failed = connector;
701 goto err;
702 }
703 }
704 mutex_unlock(&dev->mode_config.mutex);
705 return 0;
706
707err:
708 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
709 if (failed == connector)
710 break;
711
712 drm_connector_unregister(connector);
713 }
714 mutex_unlock(&dev->mode_config.mutex);
715
716 return ret;
717}
718
719static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev)
720{
721 mutex_lock(&dev->mode_config.mutex);
Daniel Vetter222b9092016-03-29 14:14:25 +0200722 drm_connector_unregister_all(dev);
Boris Brezillon1a396782015-01-06 11:13:28 +0100723 mutex_unlock(&dev->mode_config.mutex);
724}
725
Boris Brezillon1a396782015-01-06 11:13:28 +0100726static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
727{
728 struct atmel_hlcdc_dc *dc = dev->dev_private;
729
730 drm_fbdev_cma_restore_mode(dc->fbdev);
731}
732
733static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
734{
735 struct atmel_hlcdc_dc *dc = dev->dev_private;
736 unsigned int cfg = 0;
737 int i;
738
739 /* Enable interrupts on activated layers */
740 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
741 if (dc->layers[i])
742 cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
743 }
744
745 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg);
746
747 return 0;
748}
749
750static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
751{
752 struct atmel_hlcdc_dc *dc = dev->dev_private;
753 unsigned int isr;
754
755 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
756 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
757}
758
Thierry Reding88e72712015-09-24 18:35:31 +0200759static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev,
760 unsigned int pipe)
Boris Brezillon1a396782015-01-06 11:13:28 +0100761{
762 struct atmel_hlcdc_dc *dc = dev->dev_private;
763
764 /* Enable SOF (Start Of Frame) interrupt for vblank counting */
765 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
766
767 return 0;
768}
769
Thierry Reding88e72712015-09-24 18:35:31 +0200770static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev,
771 unsigned int pipe)
Boris Brezillon1a396782015-01-06 11:13:28 +0100772{
773 struct atmel_hlcdc_dc *dc = dev->dev_private;
774
775 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
776}
777
778static const struct file_operations fops = {
779 .owner = THIS_MODULE,
780 .open = drm_open,
781 .release = drm_release,
782 .unlocked_ioctl = drm_ioctl,
783#ifdef CONFIG_COMPAT
784 .compat_ioctl = drm_compat_ioctl,
785#endif
786 .poll = drm_poll,
787 .read = drm_read,
788 .llseek = no_llseek,
789 .mmap = drm_gem_cma_mmap,
790};
791
792static struct drm_driver atmel_hlcdc_dc_driver = {
Boris Brezillone14c71c2015-04-20 13:43:26 +0200793 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
Boris Brezillonaa690a92015-07-31 15:10:26 +0200794 DRIVER_MODESET | DRIVER_PRIME |
795 DRIVER_ATOMIC,
Boris Brezillon1a396782015-01-06 11:13:28 +0100796 .lastclose = atmel_hlcdc_dc_lastclose,
797 .irq_handler = atmel_hlcdc_dc_irq_handler,
798 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall,
799 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
800 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
Ville Syrjäläb44f8402015-09-30 16:46:48 +0300801 .get_vblank_counter = drm_vblank_no_hw_counter,
Boris Brezillon1a396782015-01-06 11:13:28 +0100802 .enable_vblank = atmel_hlcdc_dc_enable_vblank,
803 .disable_vblank = atmel_hlcdc_dc_disable_vblank,
804 .gem_free_object = drm_gem_cma_free_object,
805 .gem_vm_ops = &drm_gem_cma_vm_ops,
Boris Brezillone14c71c2015-04-20 13:43:26 +0200806 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
807 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
808 .gem_prime_import = drm_gem_prime_import,
809 .gem_prime_export = drm_gem_prime_export,
810 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
811 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
812 .gem_prime_vmap = drm_gem_cma_prime_vmap,
813 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
814 .gem_prime_mmap = drm_gem_cma_prime_mmap,
Boris Brezillon1a396782015-01-06 11:13:28 +0100815 .dumb_create = drm_gem_cma_dumb_create,
816 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
817 .dumb_destroy = drm_gem_dumb_destroy,
818 .fops = &fops,
819 .name = "atmel-hlcdc",
820 .desc = "Atmel HLCD Controller DRM",
821 .date = "20141504",
822 .major = 1,
823 .minor = 0,
824};
825
826static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
827{
828 struct drm_device *ddev;
829 int ret;
830
831 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
832 if (!ddev)
833 return -ENOMEM;
834
Boris Brezillon1a396782015-01-06 11:13:28 +0100835 ret = atmel_hlcdc_dc_load(ddev);
836 if (ret)
837 goto err_unref;
838
839 ret = drm_dev_register(ddev, 0);
840 if (ret)
841 goto err_unload;
842
843 ret = atmel_hlcdc_dc_connector_plug_all(ddev);
844 if (ret)
845 goto err_unregister;
846
847 return 0;
848
849err_unregister:
850 drm_dev_unregister(ddev);
851
852err_unload:
853 atmel_hlcdc_dc_unload(ddev);
854
855err_unref:
856 drm_dev_unref(ddev);
857
858 return ret;
859}
860
861static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
862{
863 struct drm_device *ddev = platform_get_drvdata(pdev);
864
865 atmel_hlcdc_dc_connector_unplug_all(ddev);
866 drm_dev_unregister(ddev);
867 atmel_hlcdc_dc_unload(ddev);
868 drm_dev_unref(ddev);
869
870 return 0;
871}
872
Thierry Redingdbb3df22015-08-14 13:58:20 +0200873#ifdef CONFIG_PM_SLEEP
Sylvain Rochet58486982015-02-22 18:51:03 +0100874static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
875{
876 struct drm_device *drm_dev = dev_get_drvdata(dev);
877 struct drm_crtc *crtc;
878
879 if (pm_runtime_suspended(dev))
880 return 0;
881
882 drm_modeset_lock_all(drm_dev);
Sylvain Rochetf026eb62015-03-12 19:47:19 +0100883 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
884 atmel_hlcdc_crtc_suspend(crtc);
Sylvain Rochet58486982015-02-22 18:51:03 +0100885 drm_modeset_unlock_all(drm_dev);
886 return 0;
887}
888
889static int atmel_hlcdc_dc_drm_resume(struct device *dev)
890{
891 struct drm_device *drm_dev = dev_get_drvdata(dev);
892 struct drm_crtc *crtc;
893
894 if (pm_runtime_suspended(dev))
895 return 0;
896
897 drm_modeset_lock_all(drm_dev);
Sylvain Rochetf026eb62015-03-12 19:47:19 +0100898 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
899 atmel_hlcdc_crtc_resume(crtc);
Sylvain Rochet58486982015-02-22 18:51:03 +0100900 drm_modeset_unlock_all(drm_dev);
901 return 0;
902}
903#endif
904
905static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
906 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume);
907
Boris Brezillon1a396782015-01-06 11:13:28 +0100908static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
909 { .compatible = "atmel,hlcdc-display-controller" },
910 { },
911};
912
913static struct platform_driver atmel_hlcdc_dc_platform_driver = {
914 .probe = atmel_hlcdc_dc_drm_probe,
915 .remove = atmel_hlcdc_dc_drm_remove,
916 .driver = {
917 .name = "atmel-hlcdc-display-controller",
Sylvain Rochet58486982015-02-22 18:51:03 +0100918 .pm = &atmel_hlcdc_dc_drm_pm_ops,
Boris Brezillon1a396782015-01-06 11:13:28 +0100919 .of_match_table = atmel_hlcdc_dc_of_match,
920 },
921};
922module_platform_driver(atmel_hlcdc_dc_platform_driver);
923
924MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
925MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
926MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
927MODULE_LICENSE("GPL");
928MODULE_ALIAS("platform:atmel-hlcdc-dc");