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 .atomic_check
= drm_atomic_helper_check
,
560 .atomic_commit
= atmel_hlcdc_dc_atomic_commit
,
563 static int atmel_hlcdc_dc_modeset_init(struct drm_device
*dev
)
565 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
568 drm_mode_config_init(dev
);
570 ret
= atmel_hlcdc_create_outputs(dev
);
572 dev_err(dev
->dev
, "failed to create HLCDC outputs: %d\n", ret
);
576 ret
= atmel_hlcdc_create_planes(dev
);
578 dev_err(dev
->dev
, "failed to create planes: %d\n", ret
);
582 ret
= atmel_hlcdc_crtc_create(dev
);
584 dev_err(dev
->dev
, "failed to create crtc\n");
588 dev
->mode_config
.min_width
= dc
->desc
->min_width
;
589 dev
->mode_config
.min_height
= dc
->desc
->min_height
;
590 dev
->mode_config
.max_width
= dc
->desc
->max_width
;
591 dev
->mode_config
.max_height
= dc
->desc
->max_height
;
592 dev
->mode_config
.funcs
= &mode_config_funcs
;
597 static int atmel_hlcdc_dc_load(struct drm_device
*dev
)
599 struct platform_device
*pdev
= to_platform_device(dev
->dev
);
600 const struct of_device_id
*match
;
601 struct atmel_hlcdc_dc
*dc
;
604 match
= of_match_node(atmel_hlcdc_of_match
, dev
->dev
->parent
->of_node
);
606 dev_err(&pdev
->dev
, "invalid compatible string\n");
611 dev_err(&pdev
->dev
, "invalid hlcdc description\n");
615 dc
= devm_kzalloc(dev
->dev
, sizeof(*dc
), GFP_KERNEL
);
619 dc
->wq
= alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
623 init_waitqueue_head(&dc
->commit
.wait
);
624 dc
->desc
= match
->data
;
625 dc
->hlcdc
= dev_get_drvdata(dev
->dev
->parent
);
626 dev
->dev_private
= dc
;
628 ret
= clk_prepare_enable(dc
->hlcdc
->periph_clk
);
630 dev_err(dev
->dev
, "failed to enable periph_clk\n");
634 pm_runtime_enable(dev
->dev
);
636 ret
= drm_vblank_init(dev
, 1);
638 dev_err(dev
->dev
, "failed to initialize vblank\n");
639 goto err_periph_clk_disable
;
642 ret
= atmel_hlcdc_dc_modeset_init(dev
);
644 dev_err(dev
->dev
, "failed to initialize mode setting\n");
645 goto err_periph_clk_disable
;
648 drm_mode_config_reset(dev
);
650 pm_runtime_get_sync(dev
->dev
);
651 ret
= drm_irq_install(dev
, dc
->hlcdc
->irq
);
652 pm_runtime_put_sync(dev
->dev
);
654 dev_err(dev
->dev
, "failed to install IRQ handler\n");
655 goto err_periph_clk_disable
;
658 platform_set_drvdata(pdev
, dev
);
660 drm_kms_helper_poll_init(dev
);
664 err_periph_clk_disable
:
665 pm_runtime_disable(dev
->dev
);
666 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
669 destroy_workqueue(dc
->wq
);
674 static void atmel_hlcdc_dc_unload(struct drm_device
*dev
)
676 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
678 flush_workqueue(dc
->wq
);
679 drm_kms_helper_poll_fini(dev
);
680 drm_atomic_helper_shutdown(dev
);
681 drm_mode_config_cleanup(dev
);
683 pm_runtime_get_sync(dev
->dev
);
684 drm_irq_uninstall(dev
);
685 pm_runtime_put_sync(dev
->dev
);
687 dev
->dev_private
= NULL
;
689 pm_runtime_disable(dev
->dev
);
690 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
691 destroy_workqueue(dc
->wq
);
694 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device
*dev
)
696 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
697 unsigned int cfg
= 0;
700 /* Enable interrupts on activated layers */
701 for (i
= 0; i
< ATMEL_HLCDC_MAX_LAYERS
; i
++) {
703 cfg
|= ATMEL_HLCDC_LAYER_STATUS(i
);
706 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IER
, cfg
);
711 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device
*dev
)
713 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
716 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IDR
, 0xffffffff);
717 regmap_read(dc
->hlcdc
->regmap
, ATMEL_HLCDC_ISR
, &isr
);
720 DEFINE_DRM_GEM_CMA_FOPS(fops
);
722 static struct drm_driver atmel_hlcdc_dc_driver
= {
723 .driver_features
= DRIVER_GEM
|
724 DRIVER_MODESET
| DRIVER_PRIME
|
726 .irq_handler
= atmel_hlcdc_dc_irq_handler
,
727 .irq_preinstall
= atmel_hlcdc_dc_irq_uninstall
,
728 .irq_postinstall
= atmel_hlcdc_dc_irq_postinstall
,
729 .irq_uninstall
= atmel_hlcdc_dc_irq_uninstall
,
730 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
731 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
732 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
733 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
734 .gem_prime_import
= drm_gem_prime_import
,
735 .gem_prime_export
= drm_gem_prime_export
,
736 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
737 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
738 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
739 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
740 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
741 .dumb_create
= drm_gem_cma_dumb_create
,
743 .name
= "atmel-hlcdc",
744 .desc
= "Atmel HLCD Controller DRM",
750 static int atmel_hlcdc_dc_drm_probe(struct platform_device
*pdev
)
752 struct drm_device
*ddev
;
755 ddev
= drm_dev_alloc(&atmel_hlcdc_dc_driver
, &pdev
->dev
);
757 return PTR_ERR(ddev
);
759 ret
= atmel_hlcdc_dc_load(ddev
);
763 ret
= drm_dev_register(ddev
, 0);
767 drm_fbdev_generic_setup(ddev
, 24);
772 atmel_hlcdc_dc_unload(ddev
);
780 static int atmel_hlcdc_dc_drm_remove(struct platform_device
*pdev
)
782 struct drm_device
*ddev
= platform_get_drvdata(pdev
);
784 drm_dev_unregister(ddev
);
785 atmel_hlcdc_dc_unload(ddev
);
791 #ifdef CONFIG_PM_SLEEP
792 static int atmel_hlcdc_dc_drm_suspend(struct device
*dev
)
794 struct drm_device
*drm_dev
= dev_get_drvdata(dev
);
795 struct atmel_hlcdc_dc
*dc
= drm_dev
->dev_private
;
796 struct regmap
*regmap
= dc
->hlcdc
->regmap
;
797 struct drm_atomic_state
*state
;
799 state
= drm_atomic_helper_suspend(drm_dev
);
801 return PTR_ERR(state
);
803 dc
->suspend
.state
= state
;
805 regmap_read(regmap
, ATMEL_HLCDC_IMR
, &dc
->suspend
.imr
);
806 regmap_write(regmap
, ATMEL_HLCDC_IDR
, dc
->suspend
.imr
);
807 clk_disable_unprepare(dc
->hlcdc
->periph_clk
);
812 static int atmel_hlcdc_dc_drm_resume(struct device
*dev
)
814 struct drm_device
*drm_dev
= dev_get_drvdata(dev
);
815 struct atmel_hlcdc_dc
*dc
= drm_dev
->dev_private
;
817 clk_prepare_enable(dc
->hlcdc
->periph_clk
);
818 regmap_write(dc
->hlcdc
->regmap
, ATMEL_HLCDC_IER
, dc
->suspend
.imr
);
820 return drm_atomic_helper_resume(drm_dev
, dc
->suspend
.state
);
824 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops
,
825 atmel_hlcdc_dc_drm_suspend
, atmel_hlcdc_dc_drm_resume
);
827 static const struct of_device_id atmel_hlcdc_dc_of_match
[] = {
828 { .compatible
= "atmel,hlcdc-display-controller" },
832 static struct platform_driver atmel_hlcdc_dc_platform_driver
= {
833 .probe
= atmel_hlcdc_dc_drm_probe
,
834 .remove
= atmel_hlcdc_dc_drm_remove
,
836 .name
= "atmel-hlcdc-display-controller",
837 .pm
= &atmel_hlcdc_dc_drm_pm_ops
,
838 .of_match_table
= atmel_hlcdc_dc_of_match
,
841 module_platform_driver(atmel_hlcdc_dc_platform_driver
);
843 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
844 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
845 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
846 MODULE_LICENSE("GPL");
847 MODULE_ALIAS("platform:atmel-hlcdc-dc");