blob: c92031f4473a9f806df440048759dafaa151d1fa [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 Brezillon348ef852015-01-07 09:30:20 +010032static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_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 .disc_pos = 5,
45 .disc_size = 6,
46 },
47 },
48 {
49 .name = "overlay1",
50 .formats = &atmel_hlcdc_plane_rgb_formats,
51 .regs_offset = 0x100,
52 .id = 1,
53 .type = ATMEL_HLCDC_OVERLAY_LAYER,
54 .nconfigs = 10,
55 .layout = {
56 .pos = 2,
57 .size = 3,
58 .xstride = { 4 },
59 .pstride = { 5 },
60 .default_color = 6,
61 .chroma_key = 7,
62 .chroma_key_mask = 8,
63 .general_config = 9,
64 },
65 },
66 {
67 .name = "high-end-overlay",
68 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
69 .regs_offset = 0x280,
70 .id = 2,
71 .type = ATMEL_HLCDC_OVERLAY_LAYER,
72 .nconfigs = 17,
73 .layout = {
74 .pos = 2,
75 .size = 3,
76 .memsize = 4,
77 .xstride = { 5, 7 },
78 .pstride = { 6, 8 },
79 .default_color = 9,
80 .chroma_key = 10,
81 .chroma_key_mask = 11,
82 .general_config = 12,
83 .csc = 14,
84 },
85 },
86 {
87 .name = "cursor",
88 .formats = &atmel_hlcdc_plane_rgb_formats,
89 .regs_offset = 0x340,
90 .id = 3,
91 .type = ATMEL_HLCDC_CURSOR_LAYER,
92 .nconfigs = 10,
93 .max_width = 128,
94 .max_height = 128,
95 .layout = {
96 .pos = 2,
97 .size = 3,
98 .xstride = { 4 },
99 .default_color = 6,
100 .chroma_key = 7,
101 .chroma_key_mask = 8,
102 .general_config = 9,
103 },
104 },
105};
106
107static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = {
108 .min_width = 0,
109 .min_height = 0,
110 .max_width = 800,
111 .max_height = 600,
112 .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers),
113 .layers = atmel_hlcdc_at91sam9x5_layers,
114};
115
Boris Brezillon1a396782015-01-06 11:13:28 +0100116static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
117 {
118 .name = "base",
119 .formats = &atmel_hlcdc_plane_rgb_formats,
120 .regs_offset = 0x40,
121 .id = 0,
122 .type = ATMEL_HLCDC_BASE_LAYER,
123 .nconfigs = 7,
124 .layout = {
125 .xstride = { 2 },
126 .default_color = 3,
127 .general_config = 4,
128 .disc_pos = 5,
129 .disc_size = 6,
130 },
131 },
132 {
133 .name = "overlay1",
134 .formats = &atmel_hlcdc_plane_rgb_formats,
135 .regs_offset = 0x140,
136 .id = 1,
137 .type = ATMEL_HLCDC_OVERLAY_LAYER,
138 .nconfigs = 10,
139 .layout = {
140 .pos = 2,
141 .size = 3,
142 .xstride = { 4 },
143 .pstride = { 5 },
144 .default_color = 6,
145 .chroma_key = 7,
146 .chroma_key_mask = 8,
147 .general_config = 9,
148 },
149 },
150 {
151 .name = "overlay2",
152 .formats = &atmel_hlcdc_plane_rgb_formats,
153 .regs_offset = 0x240,
154 .id = 2,
155 .type = ATMEL_HLCDC_OVERLAY_LAYER,
156 .nconfigs = 10,
157 .layout = {
158 .pos = 2,
159 .size = 3,
160 .xstride = { 4 },
161 .pstride = { 5 },
162 .default_color = 6,
163 .chroma_key = 7,
164 .chroma_key_mask = 8,
165 .general_config = 9,
166 },
167 },
168 {
169 .name = "high-end-overlay",
170 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
171 .regs_offset = 0x340,
172 .id = 3,
173 .type = ATMEL_HLCDC_OVERLAY_LAYER,
174 .nconfigs = 42,
175 .layout = {
176 .pos = 2,
177 .size = 3,
178 .memsize = 4,
179 .xstride = { 5, 7 },
180 .pstride = { 6, 8 },
181 .default_color = 9,
182 .chroma_key = 10,
183 .chroma_key_mask = 11,
184 .general_config = 12,
185 .csc = 14,
186 },
187 },
188 {
189 .name = "cursor",
190 .formats = &atmel_hlcdc_plane_rgb_formats,
191 .regs_offset = 0x440,
192 .id = 4,
193 .type = ATMEL_HLCDC_CURSOR_LAYER,
194 .nconfigs = 10,
195 .max_width = 128,
196 .max_height = 128,
197 .layout = {
198 .pos = 2,
199 .size = 3,
200 .xstride = { 4 },
201 .pstride = { 5 },
202 .default_color = 6,
203 .chroma_key = 7,
204 .chroma_key_mask = 8,
205 .general_config = 9,
206 },
207 },
208};
209
210static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
211 .min_width = 0,
212 .min_height = 0,
213 .max_width = 2048,
214 .max_height = 2048,
215 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
216 .layers = atmel_hlcdc_sama5d3_layers,
217};
218
219static const struct of_device_id atmel_hlcdc_of_match[] = {
220 {
Boris Brezillon348ef852015-01-07 09:30:20 +0100221 .compatible = "atmel,at91sam9x5-hlcdc",
222 .data = &atmel_hlcdc_dc_at91sam9x5,
223 },
224 {
Boris Brezillon1a396782015-01-06 11:13:28 +0100225 .compatible = "atmel,sama5d3-hlcdc",
226 .data = &atmel_hlcdc_dc_sama5d3,
227 },
228 { /* sentinel */ },
229};
230
231int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
232 struct drm_display_mode *mode)
233{
234 int vfront_porch = mode->vsync_start - mode->vdisplay;
235 int vback_porch = mode->vtotal - mode->vsync_end;
236 int vsync_len = mode->vsync_end - mode->vsync_start;
237 int hfront_porch = mode->hsync_start - mode->hdisplay;
238 int hback_porch = mode->htotal - mode->hsync_end;
239 int hsync_len = mode->hsync_end - mode->hsync_start;
240
241 if (hsync_len > 0x40 || hsync_len < 1)
242 return MODE_HSYNC;
243
244 if (vsync_len > 0x40 || vsync_len < 1)
245 return MODE_VSYNC;
246
247 if (hfront_porch > 0x200 || hfront_porch < 1 ||
248 hback_porch > 0x200 || hback_porch < 1 ||
249 mode->hdisplay < 1)
250 return MODE_H_ILLEGAL;
251
252 if (vfront_porch > 0x40 || vfront_porch < 1 ||
253 vback_porch > 0x40 || vback_porch < 0 ||
254 mode->vdisplay < 1)
255 return MODE_V_ILLEGAL;
256
257 return MODE_OK;
258}
259
260static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
261{
262 struct drm_device *dev = data;
263 struct atmel_hlcdc_dc *dc = dev->dev_private;
264 unsigned long status;
265 unsigned int imr, isr;
266 int i;
267
268 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
269 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
270 status = imr & isr;
271 if (!status)
272 return IRQ_NONE;
273
274 if (status & ATMEL_HLCDC_SOF)
275 atmel_hlcdc_crtc_irq(dc->crtc);
276
277 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
278 struct atmel_hlcdc_layer *layer = dc->layers[i];
279
280 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
281 continue;
282
283 atmel_hlcdc_layer_irq(layer);
284 }
285
286 return IRQ_HANDLED;
287}
288
289static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
290 struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
291{
292 return drm_fb_cma_create(dev, file_priv, mode_cmd);
293}
294
295static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
296{
297 struct atmel_hlcdc_dc *dc = dev->dev_private;
298
299 if (dc->fbdev) {
300 drm_fbdev_cma_hotplug_event(dc->fbdev);
301 } else {
302 dc->fbdev = drm_fbdev_cma_init(dev, 24,
303 dev->mode_config.num_crtc,
304 dev->mode_config.num_connector);
305 if (IS_ERR(dc->fbdev))
306 dc->fbdev = NULL;
307 }
308}
309
310static const struct drm_mode_config_funcs mode_config_funcs = {
311 .fb_create = atmel_hlcdc_fb_create,
312 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
Boris Brezillon2389fc12015-02-05 16:32:33 +0100313 .atomic_check = drm_atomic_helper_check,
314 .atomic_commit = drm_atomic_helper_commit,
Boris Brezillon1a396782015-01-06 11:13:28 +0100315};
316
317static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
318{
319 struct atmel_hlcdc_dc *dc = dev->dev_private;
320 struct atmel_hlcdc_planes *planes;
321 int ret;
322 int i;
323
324 drm_mode_config_init(dev);
325
326 ret = atmel_hlcdc_create_outputs(dev);
327 if (ret) {
328 dev_err(dev->dev, "failed to create panel: %d\n", ret);
329 return ret;
330 }
331
332 planes = atmel_hlcdc_create_planes(dev);
333 if (IS_ERR(planes)) {
334 dev_err(dev->dev, "failed to create planes\n");
335 return PTR_ERR(planes);
336 }
337
338 dc->planes = planes;
339
340 dc->layers[planes->primary->layer.desc->id] =
341 &planes->primary->layer;
342
343 if (planes->cursor)
344 dc->layers[planes->cursor->layer.desc->id] =
345 &planes->cursor->layer;
346
347 for (i = 0; i < planes->noverlays; i++)
348 dc->layers[planes->overlays[i]->layer.desc->id] =
349 &planes->overlays[i]->layer;
350
351 ret = atmel_hlcdc_crtc_create(dev);
352 if (ret) {
353 dev_err(dev->dev, "failed to create crtc\n");
354 return ret;
355 }
356
357 dev->mode_config.min_width = dc->desc->min_width;
358 dev->mode_config.min_height = dc->desc->min_height;
359 dev->mode_config.max_width = dc->desc->max_width;
360 dev->mode_config.max_height = dc->desc->max_height;
361 dev->mode_config.funcs = &mode_config_funcs;
362
363 return 0;
364}
365
366static int atmel_hlcdc_dc_load(struct drm_device *dev)
367{
368 struct platform_device *pdev = to_platform_device(dev->dev);
369 const struct of_device_id *match;
370 struct atmel_hlcdc_dc *dc;
371 int ret;
372
373 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
374 if (!match) {
375 dev_err(&pdev->dev, "invalid compatible string\n");
376 return -ENODEV;
377 }
378
379 if (!match->data) {
380 dev_err(&pdev->dev, "invalid hlcdc description\n");
381 return -EINVAL;
382 }
383
384 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
385 if (!dc)
386 return -ENOMEM;
387
388 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
389 if (!dc->wq)
390 return -ENOMEM;
391
392 dc->desc = match->data;
393 dc->hlcdc = dev_get_drvdata(dev->dev->parent);
394 dev->dev_private = dc;
395
396 ret = clk_prepare_enable(dc->hlcdc->periph_clk);
397 if (ret) {
398 dev_err(dev->dev, "failed to enable periph_clk\n");
399 goto err_destroy_wq;
400 }
401
402 pm_runtime_enable(dev->dev);
403
Boris Brezillon8c4b4b02015-07-16 20:55:34 +0200404 ret = drm_vblank_init(dev, 1);
405 if (ret < 0) {
406 dev_err(dev->dev, "failed to initialize vblank\n");
407 goto err_periph_clk_disable;
408 }
409
Boris Brezillon1a396782015-01-06 11:13:28 +0100410 ret = atmel_hlcdc_dc_modeset_init(dev);
411 if (ret < 0) {
412 dev_err(dev->dev, "failed to initialize mode setting\n");
413 goto err_periph_clk_disable;
414 }
415
Boris Brezillon2389fc12015-02-05 16:32:33 +0100416 drm_mode_config_reset(dev);
417
Boris Brezillon1a396782015-01-06 11:13:28 +0100418 pm_runtime_get_sync(dev->dev);
419 ret = drm_irq_install(dev, dc->hlcdc->irq);
420 pm_runtime_put_sync(dev->dev);
421 if (ret < 0) {
422 dev_err(dev->dev, "failed to install IRQ handler\n");
423 goto err_periph_clk_disable;
424 }
425
426 platform_set_drvdata(pdev, dev);
427
428 drm_kms_helper_poll_init(dev);
429
430 /* force connectors detection */
431 drm_helper_hpd_irq_event(dev);
432
433 return 0;
434
435err_periph_clk_disable:
436 pm_runtime_disable(dev->dev);
437 clk_disable_unprepare(dc->hlcdc->periph_clk);
438
439err_destroy_wq:
440 destroy_workqueue(dc->wq);
441
442 return ret;
443}
444
445static void atmel_hlcdc_dc_unload(struct drm_device *dev)
446{
447 struct atmel_hlcdc_dc *dc = dev->dev_private;
448
449 if (dc->fbdev)
450 drm_fbdev_cma_fini(dc->fbdev);
451 flush_workqueue(dc->wq);
452 drm_kms_helper_poll_fini(dev);
453 drm_mode_config_cleanup(dev);
454 drm_vblank_cleanup(dev);
455
456 pm_runtime_get_sync(dev->dev);
457 drm_irq_uninstall(dev);
458 pm_runtime_put_sync(dev->dev);
459
460 dev->dev_private = NULL;
461
462 pm_runtime_disable(dev->dev);
463 clk_disable_unprepare(dc->hlcdc->periph_clk);
464 destroy_workqueue(dc->wq);
465}
466
467static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev)
468{
469 struct drm_connector *connector, *failed;
470 int ret;
471
472 mutex_lock(&dev->mode_config.mutex);
473 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
474 ret = drm_connector_register(connector);
475 if (ret) {
476 failed = connector;
477 goto err;
478 }
479 }
480 mutex_unlock(&dev->mode_config.mutex);
481 return 0;
482
483err:
484 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
485 if (failed == connector)
486 break;
487
488 drm_connector_unregister(connector);
489 }
490 mutex_unlock(&dev->mode_config.mutex);
491
492 return ret;
493}
494
495static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev)
496{
497 mutex_lock(&dev->mode_config.mutex);
498 drm_connector_unplug_all(dev);
499 mutex_unlock(&dev->mode_config.mutex);
500}
501
502static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
503 struct drm_file *file)
504{
505 struct drm_crtc *crtc;
506
507 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
508 atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
509}
510
511static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
512{
513 struct atmel_hlcdc_dc *dc = dev->dev_private;
514
515 drm_fbdev_cma_restore_mode(dc->fbdev);
516}
517
518static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
519{
520 struct atmel_hlcdc_dc *dc = dev->dev_private;
521 unsigned int cfg = 0;
522 int i;
523
524 /* Enable interrupts on activated layers */
525 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
526 if (dc->layers[i])
527 cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
528 }
529
530 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg);
531
532 return 0;
533}
534
535static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
536{
537 struct atmel_hlcdc_dc *dc = dev->dev_private;
538 unsigned int isr;
539
540 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
541 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
542}
543
544static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc)
545{
546 struct atmel_hlcdc_dc *dc = dev->dev_private;
547
548 /* Enable SOF (Start Of Frame) interrupt for vblank counting */
549 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
550
551 return 0;
552}
553
554static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc)
555{
556 struct atmel_hlcdc_dc *dc = dev->dev_private;
557
558 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
559}
560
561static const struct file_operations fops = {
562 .owner = THIS_MODULE,
563 .open = drm_open,
564 .release = drm_release,
565 .unlocked_ioctl = drm_ioctl,
566#ifdef CONFIG_COMPAT
567 .compat_ioctl = drm_compat_ioctl,
568#endif
569 .poll = drm_poll,
570 .read = drm_read,
571 .llseek = no_llseek,
572 .mmap = drm_gem_cma_mmap,
573};
574
575static struct drm_driver atmel_hlcdc_dc_driver = {
Boris Brezillone14c71c2015-04-20 13:43:26 +0200576 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
Boris Brezillonaa690a92015-07-31 15:10:26 +0200577 DRIVER_MODESET | DRIVER_PRIME |
578 DRIVER_ATOMIC,
Boris Brezillon1a396782015-01-06 11:13:28 +0100579 .preclose = atmel_hlcdc_dc_preclose,
580 .lastclose = atmel_hlcdc_dc_lastclose,
581 .irq_handler = atmel_hlcdc_dc_irq_handler,
582 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall,
583 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
584 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
585 .get_vblank_counter = drm_vblank_count,
586 .enable_vblank = atmel_hlcdc_dc_enable_vblank,
587 .disable_vblank = atmel_hlcdc_dc_disable_vblank,
588 .gem_free_object = drm_gem_cma_free_object,
589 .gem_vm_ops = &drm_gem_cma_vm_ops,
Boris Brezillone14c71c2015-04-20 13:43:26 +0200590 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
591 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
592 .gem_prime_import = drm_gem_prime_import,
593 .gem_prime_export = drm_gem_prime_export,
594 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
595 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
596 .gem_prime_vmap = drm_gem_cma_prime_vmap,
597 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
598 .gem_prime_mmap = drm_gem_cma_prime_mmap,
Boris Brezillon1a396782015-01-06 11:13:28 +0100599 .dumb_create = drm_gem_cma_dumb_create,
600 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
601 .dumb_destroy = drm_gem_dumb_destroy,
602 .fops = &fops,
603 .name = "atmel-hlcdc",
604 .desc = "Atmel HLCD Controller DRM",
605 .date = "20141504",
606 .major = 1,
607 .minor = 0,
608};
609
610static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
611{
612 struct drm_device *ddev;
613 int ret;
614
615 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
616 if (!ddev)
617 return -ENOMEM;
618
619 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev));
620 if (ret)
621 goto err_unref;
622
623 ret = atmel_hlcdc_dc_load(ddev);
624 if (ret)
625 goto err_unref;
626
627 ret = drm_dev_register(ddev, 0);
628 if (ret)
629 goto err_unload;
630
631 ret = atmel_hlcdc_dc_connector_plug_all(ddev);
632 if (ret)
633 goto err_unregister;
634
635 return 0;
636
637err_unregister:
638 drm_dev_unregister(ddev);
639
640err_unload:
641 atmel_hlcdc_dc_unload(ddev);
642
643err_unref:
644 drm_dev_unref(ddev);
645
646 return ret;
647}
648
649static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
650{
651 struct drm_device *ddev = platform_get_drvdata(pdev);
652
653 atmel_hlcdc_dc_connector_unplug_all(ddev);
654 drm_dev_unregister(ddev);
655 atmel_hlcdc_dc_unload(ddev);
656 drm_dev_unref(ddev);
657
658 return 0;
659}
660
Sylvain Rochet58486982015-02-22 18:51:03 +0100661#ifdef CONFIG_PM
662static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
663{
664 struct drm_device *drm_dev = dev_get_drvdata(dev);
665 struct drm_crtc *crtc;
666
667 if (pm_runtime_suspended(dev))
668 return 0;
669
670 drm_modeset_lock_all(drm_dev);
Sylvain Rochetf026eb62015-03-12 19:47:19 +0100671 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
672 atmel_hlcdc_crtc_suspend(crtc);
Sylvain Rochet58486982015-02-22 18:51:03 +0100673 drm_modeset_unlock_all(drm_dev);
674 return 0;
675}
676
677static int atmel_hlcdc_dc_drm_resume(struct device *dev)
678{
679 struct drm_device *drm_dev = dev_get_drvdata(dev);
680 struct drm_crtc *crtc;
681
682 if (pm_runtime_suspended(dev))
683 return 0;
684
685 drm_modeset_lock_all(drm_dev);
Sylvain Rochetf026eb62015-03-12 19:47:19 +0100686 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
687 atmel_hlcdc_crtc_resume(crtc);
Sylvain Rochet58486982015-02-22 18:51:03 +0100688 drm_modeset_unlock_all(drm_dev);
689 return 0;
690}
691#endif
692
693static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
694 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume);
695
Boris Brezillon1a396782015-01-06 11:13:28 +0100696static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
697 { .compatible = "atmel,hlcdc-display-controller" },
698 { },
699};
700
701static struct platform_driver atmel_hlcdc_dc_platform_driver = {
702 .probe = atmel_hlcdc_dc_drm_probe,
703 .remove = atmel_hlcdc_dc_drm_remove,
704 .driver = {
705 .name = "atmel-hlcdc-display-controller",
Sylvain Rochet58486982015-02-22 18:51:03 +0100706 .pm = &atmel_hlcdc_dc_drm_pm_ops,
Boris Brezillon1a396782015-01-06 11:13:28 +0100707 .of_match_table = atmel_hlcdc_dc_of_match,
708 },
709};
710module_platform_driver(atmel_hlcdc_dc_platform_driver);
711
712MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
713MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
714MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
715MODULE_LICENSE("GPL");
716MODULE_ALIAS("platform:atmel-hlcdc-dc");