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