2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
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.
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
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/>.
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>
28 #include "atmel_hlcdc_dc.h"
30 #define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8
32 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers
[] = {
35 .formats
= &atmel_hlcdc_plane_rgb_formats
,
38 .type
= ATMEL_HLCDC_BASE_LAYER
,
49 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12
= {
57 .conflicting_output_formats
= true,
58 .nlayers
= ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers
),
59 .layers
= atmel_hlcdc_at91sam9n12_layers
,
62 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers
[] = {
65 .formats
= &atmel_hlcdc_plane_rgb_formats
,
68 .type
= ATMEL_HLCDC_BASE_LAYER
,
81 .formats
= &atmel_hlcdc_plane_rgb_formats
,
84 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
99 .name
= "high-end-overlay",
100 .formats
= &atmel_hlcdc_plane_rgb_and_yuv_formats
,
101 .regs_offset
= 0x280,
103 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
113 .chroma_key_mask
= 11,
114 .general_config
= 12,
118 .clut_offset
= 0x1000,
122 .formats
= &atmel_hlcdc_plane_rgb_formats
,
123 .regs_offset
= 0x340,
125 .type
= ATMEL_HLCDC_CURSOR_LAYER
,
135 .chroma_key_mask
= 8,
138 .clut_offset
= 0x1400,
142 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5
= {
150 .conflicting_output_formats
= true,
151 .nlayers
= ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers
),
152 .layers
= atmel_hlcdc_at91sam9x5_layers
,
155 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers
[] = {
158 .formats
= &atmel_hlcdc_plane_rgb_formats
,
161 .type
= ATMEL_HLCDC_BASE_LAYER
,
170 .clut_offset
= 0x600,
174 .formats
= &atmel_hlcdc_plane_rgb_formats
,
175 .regs_offset
= 0x140,
177 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
186 .chroma_key_mask
= 8,
189 .clut_offset
= 0xa00,
193 .formats
= &atmel_hlcdc_plane_rgb_formats
,
194 .regs_offset
= 0x240,
196 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
205 .chroma_key_mask
= 8,
208 .clut_offset
= 0xe00,
211 .name
= "high-end-overlay",
212 .formats
= &atmel_hlcdc_plane_rgb_and_yuv_formats
,
213 .regs_offset
= 0x340,
215 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
225 .chroma_key_mask
= 11,
226 .general_config
= 12,
234 .clut_offset
= 0x1200,
238 .formats
= &atmel_hlcdc_plane_rgb_formats
,
239 .regs_offset
= 0x440,
241 .type
= ATMEL_HLCDC_CURSOR_LAYER
,
252 .chroma_key_mask
= 8,
256 .clut_offset
= 0x1600,
260 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3
= {
268 .conflicting_output_formats
= true,
269 .nlayers
= ARRAY_SIZE(atmel_hlcdc_sama5d3_layers
),
270 .layers
= atmel_hlcdc_sama5d3_layers
,
273 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers
[] = {
276 .formats
= &atmel_hlcdc_plane_rgb_formats
,
279 .type
= ATMEL_HLCDC_BASE_LAYER
,
288 .clut_offset
= 0x600,
292 .formats
= &atmel_hlcdc_plane_rgb_formats
,
293 .regs_offset
= 0x140,
295 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
304 .chroma_key_mask
= 8,
307 .clut_offset
= 0xa00,
311 .formats
= &atmel_hlcdc_plane_rgb_formats
,
312 .regs_offset
= 0x240,
314 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
323 .chroma_key_mask
= 8,
326 .clut_offset
= 0xe00,
329 .name
= "high-end-overlay",
330 .formats
= &atmel_hlcdc_plane_rgb_and_yuv_formats
,
331 .regs_offset
= 0x340,
333 .type
= ATMEL_HLCDC_OVERLAY_LAYER
,
343 .chroma_key_mask
= 11,
344 .general_config
= 12,
352 .clut_offset
= 0x1200,
356 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4
= {
364 .nlayers
= ARRAY_SIZE(atmel_hlcdc_sama5d4_layers
),
365 .layers
= atmel_hlcdc_sama5d4_layers
,
367 static const struct of_device_id atmel_hlcdc_of_match
[] = {
369 .compatible
= "atmel,at91sam9n12-hlcdc",
370 .data
= &atmel_hlcdc_dc_at91sam9n12
,
373 .compatible
= "atmel,at91sam9x5-hlcdc",
374 .data
= &atmel_hlcdc_dc_at91sam9x5
,
377 .compatible
= "atmel,sama5d2-hlcdc",
378 .data
= &atmel_hlcdc_dc_sama5d4
,
381 .compatible
= "atmel,sama5d3-hlcdc",
382 .data
= &atmel_hlcdc_dc_sama5d3
,
385 .compatible
= "atmel,sama5d4-hlcdc",
386 .data
= &atmel_hlcdc_dc_sama5d4
,
390 MODULE_DEVICE_TABLE(of
, atmel_hlcdc_of_match
);
393 atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc
*dc
,
394 const struct drm_display_mode
*mode
)
396 int vfront_porch
= mode
->vsync_start
- mode
->vdisplay
;
397 int vback_porch
= mode
->vtotal
- mode
->vsync_end
;
398 int vsync_len
= mode
->vsync_end
- mode
->vsync_start
;
399 int hfront_porch
= mode
->hsync_start
- mode
->hdisplay
;
400 int hback_porch
= mode
->htotal
- mode
->hsync_end
;
401 int hsync_len
= mode
->hsync_end
- mode
->hsync_start
;
403 if (hsync_len
> dc
->desc
->max_spw
+ 1 || hsync_len
< 1)
406 if (vsync_len
> dc
->desc
->max_spw
+ 1 || vsync_len
< 1)
409 if (hfront_porch
> dc
->desc
->max_hpw
+ 1 || hfront_porch
< 1 ||
410 hback_porch
> dc
->desc
->max_hpw
+ 1 || hback_porch
< 1 ||
412 return MODE_H_ILLEGAL
;
414 if (vfront_porch
> dc
->desc
->max_vpw
+ 1 || vfront_porch
< 1 ||
415 vback_porch
> dc
->desc
->max_vpw
|| vback_porch
< 0 ||
417 return MODE_V_ILLEGAL
;
422 static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer
*layer
)
427 if (layer
->desc
->type
== ATMEL_HLCDC_BASE_LAYER
||
428 layer
->desc
->type
== ATMEL_HLCDC_OVERLAY_LAYER
||
429 layer
->desc
->type
== ATMEL_HLCDC_CURSOR_LAYER
)
430 atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer
));
433 static irqreturn_t
atmel_hlcdc_dc_irq_handler(int irq
, void *data
)
435 struct drm_device
*dev
= data
;
436 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
437 unsigned long status
;
438 unsigned int imr
, isr
;
441 regmap_read(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IMR
, &imr
);
442 regmap_read(dc
->hlcdc
->regmap
, ATMEL_HLCDC_ISR
, &isr
);
447 if (status
& ATMEL_HLCDC_SOF
)
448 atmel_hlcdc_crtc_irq(dc
->crtc
);
450 for (i
= 0; i
< ATMEL_HLCDC_MAX_LAYERS
; i
++) {
451 if (ATMEL_HLCDC_LAYER_STATUS(i
) & status
)
452 atmel_hlcdc_layer_irq(dc
->layers
[i
]);
458 static struct drm_framebuffer
*atmel_hlcdc_fb_create(struct drm_device
*dev
,
459 struct drm_file
*file_priv
, const struct drm_mode_fb_cmd2
*mode_cmd
)
461 return drm_gem_fb_create(dev
, file_priv
, mode_cmd
);
464 struct atmel_hlcdc_dc_commit
{
465 struct work_struct work
;
466 struct drm_device
*dev
;
467 struct drm_atomic_state
*state
;
471 atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit
*commit
)
473 struct drm_device
*dev
= commit
->dev
;
474 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
475 struct drm_atomic_state
*old_state
= commit
->state
;
477 /* Apply the atomic update. */
478 drm_atomic_helper_commit_modeset_disables(dev
, old_state
);
479 drm_atomic_helper_commit_planes(dev
, old_state
, 0);
480 drm_atomic_helper_commit_modeset_enables(dev
, old_state
);
482 drm_atomic_helper_wait_for_vblanks(dev
, old_state
);
484 drm_atomic_helper_cleanup_planes(dev
, old_state
);
486 drm_atomic_state_put(old_state
);
488 /* Complete the commit, wake up any waiter. */
489 spin_lock(&dc
->commit
.wait
.lock
);
490 dc
->commit
.pending
= false;
491 wake_up_all_locked(&dc
->commit
.wait
);
492 spin_unlock(&dc
->commit
.wait
.lock
);
497 static void atmel_hlcdc_dc_atomic_work(struct work_struct
*work
)
499 struct atmel_hlcdc_dc_commit
*commit
=
500 container_of(work
, struct atmel_hlcdc_dc_commit
, work
);
502 atmel_hlcdc_dc_atomic_complete(commit
);
505 static int atmel_hlcdc_dc_atomic_commit(struct drm_device
*dev
,
506 struct drm_atomic_state
*state
,
509 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
510 struct atmel_hlcdc_dc_commit
*commit
;
513 ret
= drm_atomic_helper_prepare_planes(dev
, state
);
517 /* Allocate the commit object. */
518 commit
= kzalloc(sizeof(*commit
), GFP_KERNEL
);
524 INIT_WORK(&commit
->work
, atmel_hlcdc_dc_atomic_work
);
526 commit
->state
= state
;
528 spin_lock(&dc
->commit
.wait
.lock
);
529 ret
= wait_event_interruptible_locked(dc
->commit
.wait
,
530 !dc
->commit
.pending
);
532 dc
->commit
.pending
= true;
533 spin_unlock(&dc
->commit
.wait
.lock
);
538 /* We have our own synchronization through the commit lock. */
539 BUG_ON(drm_atomic_helper_swap_state(state
, false) < 0);
541 /* Swap state succeeded, this is the point of no return. */
542 drm_atomic_state_get(state
);
544 queue_work(dc
->wq
, &commit
->work
);
546 atmel_hlcdc_dc_atomic_complete(commit
);
553 drm_atomic_helper_cleanup_planes(dev
, state
);
557 static const struct drm_mode_config_funcs mode_config_funcs
= {
558 .fb_create
= atmel_hlcdc_fb_create
,
559 .output_poll_changed
= drm_fb_helper_output_poll_changed
,
560 .atomic_check
= drm_atomic_helper_check
,
561 .atomic_commit
= atmel_hlcdc_dc_atomic_commit
,
564 static int atmel_hlcdc_dc_modeset_init(struct drm_device
*dev
)
566 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
569 drm_mode_config_init(dev
);
571 ret
= atmel_hlcdc_create_outputs(dev
);
573 dev_err(dev
->dev
, "failed to create HLCDC outputs: %d\n", ret
);
577 ret
= atmel_hlcdc_create_planes(dev
);
579 dev_err(dev
->dev
, "failed to create planes: %d\n", ret
);
583 ret
= atmel_hlcdc_crtc_create(dev
);
585 dev_err(dev
->dev
, "failed to create crtc\n");
589 dev
->mode_config
.min_width
= dc
->desc
->min_width
;
590 dev
->mode_config
.min_height
= dc
->desc
->min_height
;
591 dev
->mode_config
.max_width
= dc
->desc
->max_width
;
592 dev
->mode_config
.max_height
= dc
->desc
->max_height
;
593 dev
->mode_config
.funcs
= &mode_config_funcs
;
598 static int atmel_hlcdc_dc_load(struct drm_device
*dev
)
600 struct platform_device
*pdev
= to_platform_device(dev
->dev
);
601 const struct of_device_id
*match
;
602 struct atmel_hlcdc_dc
*dc
;
605 match
= of_match_node(atmel_hlcdc_of_match
, dev
->dev
->parent
->of_node
);
607 dev_err(&pdev
->dev
, "invalid compatible string\n");
612 dev_err(&pdev
->dev
, "invalid hlcdc description\n");
616 dc
= devm_kzalloc(dev
->dev
, sizeof(*dc
), GFP_KERNEL
);
620 dc
->wq
= alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
624 init_waitqueue_head(&dc
->commit
.wait
);
625 dc
->desc
= match
->data
;
626 dc
->hlcdc
= dev_get_drvdata(dev
->dev
->parent
);
627 dev
->dev_private
= dc
;
629 ret
= clk_prepare_enable(dc
->hlcdc
->periph_clk
);
631 dev_err(dev
->dev
, "failed to enable periph_clk\n");
635 pm_runtime_enable(dev
->dev
);
637 ret
= drm_vblank_init(dev
, 1);
639 dev_err(dev
->dev
, "failed to initialize vblank\n");
640 goto err_periph_clk_disable
;
643 ret
= atmel_hlcdc_dc_modeset_init(dev
);
645 dev_err(dev
->dev
, "failed to initialize mode setting\n");
646 goto err_periph_clk_disable
;
649 drm_mode_config_reset(dev
);
651 pm_runtime_get_sync(dev
->dev
);
652 ret
= drm_irq_install(dev
, dc
->hlcdc
->irq
);
653 pm_runtime_put_sync(dev
->dev
);
655 dev_err(dev
->dev
, "failed to install IRQ handler\n");
656 goto err_periph_clk_disable
;
659 platform_set_drvdata(pdev
, dev
);
661 drm_fb_cma_fbdev_init(dev
, 24, 0);
663 drm_kms_helper_poll_init(dev
);
667 err_periph_clk_disable
:
668 pm_runtime_disable(dev
->dev
);
669 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
672 destroy_workqueue(dc
->wq
);
677 static void atmel_hlcdc_dc_unload(struct drm_device
*dev
)
679 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
681 drm_fb_cma_fbdev_fini(dev
);
682 flush_workqueue(dc
->wq
);
683 drm_kms_helper_poll_fini(dev
);
684 drm_atomic_helper_shutdown(dev
);
685 drm_mode_config_cleanup(dev
);
687 pm_runtime_get_sync(dev
->dev
);
688 drm_irq_uninstall(dev
);
689 pm_runtime_put_sync(dev
->dev
);
691 dev
->dev_private
= NULL
;
693 pm_runtime_disable(dev
->dev
);
694 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
695 destroy_workqueue(dc
->wq
);
698 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device
*dev
)
700 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
701 unsigned int cfg
= 0;
704 /* Enable interrupts on activated layers */
705 for (i
= 0; i
< ATMEL_HLCDC_MAX_LAYERS
; i
++) {
707 cfg
|= ATMEL_HLCDC_LAYER_STATUS(i
);
710 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IER
, cfg
);
715 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device
*dev
)
717 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
720 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IDR
, 0xffffffff);
721 regmap_read(dc
->hlcdc
->regmap
, ATMEL_HLCDC_ISR
, &isr
);
724 DEFINE_DRM_GEM_CMA_FOPS(fops
);
726 static struct drm_driver atmel_hlcdc_dc_driver
= {
727 .driver_features
= DRIVER_HAVE_IRQ
| DRIVER_GEM
|
728 DRIVER_MODESET
| DRIVER_PRIME
|
730 .lastclose
= drm_fb_helper_lastclose
,
731 .irq_handler
= atmel_hlcdc_dc_irq_handler
,
732 .irq_preinstall
= atmel_hlcdc_dc_irq_uninstall
,
733 .irq_postinstall
= atmel_hlcdc_dc_irq_postinstall
,
734 .irq_uninstall
= atmel_hlcdc_dc_irq_uninstall
,
735 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
736 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
737 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
738 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
739 .gem_prime_import
= drm_gem_prime_import
,
740 .gem_prime_export
= drm_gem_prime_export
,
741 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
742 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
743 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
744 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
745 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
746 .dumb_create
= drm_gem_cma_dumb_create
,
748 .name
= "atmel-hlcdc",
749 .desc
= "Atmel HLCD Controller DRM",
755 static int atmel_hlcdc_dc_drm_probe(struct platform_device
*pdev
)
757 struct drm_device
*ddev
;
760 ddev
= drm_dev_alloc(&atmel_hlcdc_dc_driver
, &pdev
->dev
);
762 return PTR_ERR(ddev
);
764 ret
= atmel_hlcdc_dc_load(ddev
);
768 ret
= drm_dev_register(ddev
, 0);
775 atmel_hlcdc_dc_unload(ddev
);
783 static int atmel_hlcdc_dc_drm_remove(struct platform_device
*pdev
)
785 struct drm_device
*ddev
= platform_get_drvdata(pdev
);
787 drm_dev_unregister(ddev
);
788 atmel_hlcdc_dc_unload(ddev
);
794 #ifdef CONFIG_PM_SLEEP
795 static int atmel_hlcdc_dc_drm_suspend(struct device
*dev
)
797 struct drm_device
*drm_dev
= dev_get_drvdata(dev
);
798 struct atmel_hlcdc_dc
*dc
= drm_dev
->dev_private
;
799 struct regmap
*regmap
= dc
->hlcdc
->regmap
;
800 struct drm_atomic_state
*state
;
802 state
= drm_atomic_helper_suspend(drm_dev
);
804 return PTR_ERR(state
);
806 dc
->suspend
.state
= state
;
808 regmap_read(regmap
, ATMEL_HLCDC_IMR
, &dc
->suspend
.imr
);
809 regmap_write(regmap
, ATMEL_HLCDC_IDR
, dc
->suspend
.imr
);
810 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
815 static int atmel_hlcdc_dc_drm_resume(struct device
*dev
)
817 struct drm_device
*drm_dev
= dev_get_drvdata(dev
);
818 struct atmel_hlcdc_dc
*dc
= drm_dev
->dev_private
;
820 clk_prepare_enable(dc
->hlcdc
->periph_clk
);
821 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IER
, dc
->suspend
.imr
);
823 return drm_atomic_helper_resume(drm_dev
, dc
->suspend
.state
);
827 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops
,
828 atmel_hlcdc_dc_drm_suspend
, atmel_hlcdc_dc_drm_resume
);
830 static const struct of_device_id atmel_hlcdc_dc_of_match
[] = {
831 { .compatible
= "atmel,hlcdc-display-controller" },
835 static struct platform_driver atmel_hlcdc_dc_platform_driver
= {
836 .probe
= atmel_hlcdc_dc_drm_probe
,
837 .remove
= atmel_hlcdc_dc_drm_remove
,
839 .name
= "atmel-hlcdc-display-controller",
840 .pm
= &atmel_hlcdc_dc_drm_pm_ops
,
841 .of_match_table
= atmel_hlcdc_dc_of_match
,
844 module_platform_driver(atmel_hlcdc_dc_platform_driver
);
846 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
847 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
848 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
849 MODULE_LICENSE("GPL");
850 MODULE_ALIAS("platform:atmel-hlcdc-dc");