12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21c248b7dSInki Dae /* exynos_drm_crtc.c
31c248b7dSInki Dae  *
41c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
51c248b7dSInki Dae  * Authors:
61c248b7dSInki Dae  *	Inki Dae <inki.dae@samsung.com>
71c248b7dSInki Dae  *	Joonyoung Shim <jy0922.shim@samsung.com>
81c248b7dSInki Dae  *	Seung-Woo Kim <sw0312.kim@samsung.com>
91c248b7dSInki Dae  */
101c248b7dSInki Dae 
114ea9526bSGustavo Padovan #include <drm/drm_atomic.h>
124ea9526bSGustavo Padovan #include <drm/drm_atomic_helper.h>
131ca582f1SAndrzej Hajda #include <drm/drm_encoder.h>
14fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
152bda34d7SSam Ravnborg #include <drm/drm_vblank.h>
161c248b7dSInki Dae 
17e30655d0SMark Brown #include "exynos_drm_crtc.h"
181c248b7dSInki Dae #include "exynos_drm_drv.h"
19b5d2eb3bSJoonyoung Shim #include "exynos_drm_plane.h"
201c248b7dSInki Dae 
exynos_drm_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)210b20a0f8SLaurent Pinchart static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc,
22351f950dSMaxime Ripard 					  struct drm_atomic_state *state)
231c248b7dSInki Dae {
24d2716c89SJoonyoung Shim 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
251c248b7dSInki Dae 
2611f95489SInki Dae 	if (exynos_crtc->ops->atomic_enable)
2711f95489SInki Dae 		exynos_crtc->ops->atomic_enable(exynos_crtc);
28080be03dSSean Paul 
29d6948b2fSAndrzej Hajda 	drm_crtc_vblank_on(crtc);
301c248b7dSInki Dae }
311c248b7dSInki Dae 
exynos_drm_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)3264581714SLaurent Pinchart static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc,
33351f950dSMaxime Ripard 					   struct drm_atomic_state *state)
343fc4867cSGustavo Padovan {
3563498e30SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
363fc4867cSGustavo Padovan 
3763498e30SGustavo Padovan 	drm_crtc_vblank_off(crtc);
3863498e30SGustavo Padovan 
3911f95489SInki Dae 	if (exynos_crtc->ops->atomic_disable)
4011f95489SInki Dae 		exynos_crtc->ops->atomic_disable(exynos_crtc);
4141cbf0fdSInki Dae 
4241cbf0fdSInki Dae 	spin_lock_irq(&crtc->dev->event_lock);
43*2e63972aSTuo Li 	if (crtc->state->event && !crtc->state->active) {
4441cbf0fdSInki Dae 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
4541cbf0fdSInki Dae 		crtc->state->event = NULL;
4641cbf0fdSInki Dae 	}
47*2e63972aSTuo Li 	spin_unlock_irq(&crtc->dev->event_lock);
483fc4867cSGustavo Padovan }
493fc4867cSGustavo Padovan 
exynos_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)505625b341SAndrzej Hajda static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
5129b77ad7SMaxime Ripard 				     struct drm_atomic_state *state)
525625b341SAndrzej Hajda {
5329b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
5429b77ad7SMaxime Ripard 									  crtc);
555625b341SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
565625b341SAndrzej Hajda 
5729b77ad7SMaxime Ripard 	if (!crtc_state->enable)
58c4e07407SAndrzej Hajda 		return 0;
59c4e07407SAndrzej Hajda 
605625b341SAndrzej Hajda 	if (exynos_crtc->ops->atomic_check)
6129b77ad7SMaxime Ripard 		return exynos_crtc->ops->atomic_check(exynos_crtc, crtc_state);
625625b341SAndrzej Hajda 
635625b341SAndrzej Hajda 	return 0;
645625b341SAndrzej Hajda }
655625b341SAndrzej Hajda 
exynos_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)66613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
67f6ebe9f9SMaxime Ripard 				     struct drm_atomic_state *state)
689d5ab6a0SGustavo Padovan {
699d5ab6a0SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
709d5ab6a0SGustavo Padovan 
71d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_begin)
72d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_begin(exynos_crtc);
739d5ab6a0SGustavo Padovan }
749d5ab6a0SGustavo Padovan 
exynos_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)75613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
76f6ebe9f9SMaxime Ripard 				     struct drm_atomic_state *state)
779d5ab6a0SGustavo Padovan {
78d9220d47SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
79d9220d47SGustavo Padovan 
80d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_flush)
81d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_flush(exynos_crtc);
829d5ab6a0SGustavo Padovan }
839d5ab6a0SGustavo Padovan 
exynos_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)84c3653fedSAndrzej Hajda static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
85c3653fedSAndrzej Hajda 	const struct drm_display_mode *mode)
86c3653fedSAndrzej Hajda {
87c3653fedSAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
88c3653fedSAndrzej Hajda 
89c3653fedSAndrzej Hajda 	if (exynos_crtc->ops->mode_valid)
90c3653fedSAndrzej Hajda 		return exynos_crtc->ops->mode_valid(exynos_crtc, mode);
91c3653fedSAndrzej Hajda 
92c3653fedSAndrzej Hajda 	return MODE_OK;
93c3653fedSAndrzej Hajda }
94c3653fedSAndrzej Hajda 
exynos_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)952466db97SAndrzej Hajda static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc,
962466db97SAndrzej Hajda 		const struct drm_display_mode *mode,
972466db97SAndrzej Hajda 		struct drm_display_mode *adjusted_mode)
982466db97SAndrzej Hajda {
992466db97SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1002466db97SAndrzej Hajda 
1012466db97SAndrzej Hajda 	if (exynos_crtc->ops->mode_fixup)
1022466db97SAndrzej Hajda 		return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
1032466db97SAndrzej Hajda 				adjusted_mode);
1042466db97SAndrzej Hajda 
1052466db97SAndrzej Hajda 	return true;
1062466db97SAndrzej Hajda }
1072466db97SAndrzej Hajda 
1082466db97SAndrzej Hajda 
109800ba2b5SVille Syrjälä static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
110c3653fedSAndrzej Hajda 	.mode_valid	= exynos_crtc_mode_valid,
1112466db97SAndrzej Hajda 	.mode_fixup	= exynos_crtc_mode_fixup,
1125625b341SAndrzej Hajda 	.atomic_check	= exynos_crtc_atomic_check,
1139d5ab6a0SGustavo Padovan 	.atomic_begin	= exynos_crtc_atomic_begin,
1149d5ab6a0SGustavo Padovan 	.atomic_flush	= exynos_crtc_atomic_flush,
1150b20a0f8SLaurent Pinchart 	.atomic_enable	= exynos_drm_crtc_atomic_enable,
11664581714SLaurent Pinchart 	.atomic_disable	= exynos_drm_crtc_atomic_disable,
1171c248b7dSInki Dae };
1181c248b7dSInki Dae 
exynos_crtc_handle_event(struct exynos_drm_crtc * exynos_crtc)119a392276dSAndrzej Hajda void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
120a392276dSAndrzej Hajda {
121a392276dSAndrzej Hajda 	struct drm_crtc *crtc = &exynos_crtc->base;
122a392276dSAndrzej Hajda 	struct drm_pending_vblank_event *event = crtc->state->event;
123a392276dSAndrzej Hajda 	unsigned long flags;
124a392276dSAndrzej Hajda 
12573b7b44fSAndrzej Hajda 	if (!event)
12673b7b44fSAndrzej Hajda 		return;
127a392276dSAndrzej Hajda 	crtc->state->event = NULL;
128a392276dSAndrzej Hajda 
12973b7b44fSAndrzej Hajda 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
13073b7b44fSAndrzej Hajda 
13173b7b44fSAndrzej Hajda 	spin_lock_irqsave(&crtc->dev->event_lock, flags);
13273b7b44fSAndrzej Hajda 	drm_crtc_arm_vblank_event(crtc, event);
13373b7b44fSAndrzej Hajda 	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
134a392276dSAndrzej Hajda }
135a392276dSAndrzej Hajda 
exynos_drm_crtc_destroy(struct drm_crtc * crtc)1361c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
1371c248b7dSInki Dae {
1381c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1391c248b7dSInki Dae 
1401c248b7dSInki Dae 	drm_crtc_cleanup(crtc);
1411c248b7dSInki Dae 	kfree(exynos_crtc);
1421c248b7dSInki Dae }
1431c248b7dSInki Dae 
exynos_drm_crtc_enable_vblank(struct drm_crtc * crtc)14464b0e1d6SShawn Guo static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc)
14564b0e1d6SShawn Guo {
14664b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
14764b0e1d6SShawn Guo 
14864b0e1d6SShawn Guo 	if (exynos_crtc->ops->enable_vblank)
14964b0e1d6SShawn Guo 		return exynos_crtc->ops->enable_vblank(exynos_crtc);
15064b0e1d6SShawn Guo 
15164b0e1d6SShawn Guo 	return 0;
15264b0e1d6SShawn Guo }
15364b0e1d6SShawn Guo 
exynos_drm_crtc_disable_vblank(struct drm_crtc * crtc)15464b0e1d6SShawn Guo static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
15564b0e1d6SShawn Guo {
15664b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
15764b0e1d6SShawn Guo 
15864b0e1d6SShawn Guo 	if (exynos_crtc->ops->disable_vblank)
15964b0e1d6SShawn Guo 		exynos_crtc->ops->disable_vblank(exynos_crtc);
16064b0e1d6SShawn Guo }
16164b0e1d6SShawn Guo 
162800ba2b5SVille Syrjälä static const struct drm_crtc_funcs exynos_crtc_funcs = {
16347a7deffSGustavo Padovan 	.set_config	= drm_atomic_helper_set_config,
1649d5ab6a0SGustavo Padovan 	.page_flip	= drm_atomic_helper_page_flip,
1651c248b7dSInki Dae 	.destroy	= exynos_drm_crtc_destroy,
1664ea9526bSGustavo Padovan 	.reset = drm_atomic_helper_crtc_reset,
1674ea9526bSGustavo Padovan 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1684ea9526bSGustavo Padovan 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
16964b0e1d6SShawn Guo 	.enable_vblank = exynos_drm_crtc_enable_vblank,
17064b0e1d6SShawn Guo 	.disable_vblank = exynos_drm_crtc_disable_vblank,
1711c248b7dSInki Dae };
1721c248b7dSInki Dae 
exynos_drm_crtc_create(struct drm_device * drm_dev,struct drm_plane * plane,enum exynos_drm_output_type type,const struct exynos_drm_crtc_ops * ops,void * ctx)17393bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
1747ee14cdcSGustavo Padovan 					struct drm_plane *plane,
17593bca243SGustavo Padovan 					enum exynos_drm_output_type type,
176f3aaf762SKrzysztof Kozlowski 					const struct exynos_drm_crtc_ops *ops,
17793bca243SGustavo Padovan 					void *ctx)
1781c248b7dSInki Dae {
1791c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc;
1801c248b7dSInki Dae 	struct drm_crtc *crtc;
18172ed6ccdSAndrzej Hajda 	int ret;
1821c248b7dSInki Dae 
1831c248b7dSInki Dae 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
18438bb5253SSachin Kamat 	if (!exynos_crtc)
18593bca243SGustavo Padovan 		return ERR_PTR(-ENOMEM);
1861c248b7dSInki Dae 
1875d1741adSGustavo Padovan 	exynos_crtc->type = type;
18893bca243SGustavo Padovan 	exynos_crtc->ops = ops;
18993bca243SGustavo Padovan 	exynos_crtc->ctx = ctx;
190b5d2eb3bSJoonyoung Shim 
191357193cdSGustavo Padovan 	crtc = &exynos_crtc->base;
1921c248b7dSInki Dae 
193eb88e422SGustavo Padovan 	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
194f9882876SVille Syrjälä 					&exynos_crtc_funcs, NULL);
19572ed6ccdSAndrzej Hajda 	if (ret < 0)
19672ed6ccdSAndrzej Hajda 		goto err_crtc;
19772ed6ccdSAndrzej Hajda 
1981c248b7dSInki Dae 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
1991c248b7dSInki Dae 
20093bca243SGustavo Padovan 	return exynos_crtc;
20172ed6ccdSAndrzej Hajda 
20272ed6ccdSAndrzej Hajda err_crtc:
20372ed6ccdSAndrzej Hajda 	plane->funcs->destroy(plane);
20472ed6ccdSAndrzej Hajda 	kfree(exynos_crtc);
20593bca243SGustavo Padovan 	return ERR_PTR(ret);
2061c248b7dSInki Dae }
2071c248b7dSInki Dae 
exynos_drm_crtc_get_by_type(struct drm_device * drm_dev,enum exynos_drm_output_type out_type)2081ca582f1SAndrzej Hajda struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
209cf67cc9aSGustavo Padovan 				       enum exynos_drm_output_type out_type)
210f37cd5e8SInki Dae {
211f37cd5e8SInki Dae 	struct drm_crtc *crtc;
212f37cd5e8SInki Dae 
213d644951cSAndrzej Hajda 	drm_for_each_crtc(crtc, drm_dev)
214d644951cSAndrzej Hajda 		if (to_exynos_crtc(crtc)->type == out_type)
2151ca582f1SAndrzej Hajda 			return to_exynos_crtc(crtc);
216f37cd5e8SInki Dae 
217e9497dc2SMarek Szyprowski 	return ERR_PTR(-ENODEV);
2181ca582f1SAndrzej Hajda }
2191ca582f1SAndrzej Hajda 
exynos_drm_set_possible_crtcs(struct drm_encoder * encoder,enum exynos_drm_output_type out_type)2201ca582f1SAndrzej Hajda int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
2211ca582f1SAndrzej Hajda 		enum exynos_drm_output_type out_type)
2221ca582f1SAndrzej Hajda {
2231ca582f1SAndrzej Hajda 	struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev,
2241ca582f1SAndrzej Hajda 						out_type);
2251ca582f1SAndrzej Hajda 
2261ca582f1SAndrzej Hajda 	if (IS_ERR(crtc))
2271ca582f1SAndrzej Hajda 		return PTR_ERR(crtc);
2281ca582f1SAndrzej Hajda 
2291ca582f1SAndrzej Hajda 	encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
2301ca582f1SAndrzej Hajda 
2311ca582f1SAndrzej Hajda 	return 0;
232f37cd5e8SInki Dae }
2335595d4d8SYoungJun Cho 
exynos_drm_crtc_te_handler(struct drm_crtc * crtc)2345595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
2355595d4d8SYoungJun Cho {
23693bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
2375595d4d8SYoungJun Cho 
23893bca243SGustavo Padovan 	if (exynos_crtc->ops->te_handler)
23993bca243SGustavo Padovan 		exynos_crtc->ops->te_handler(exynos_crtc);
2405595d4d8SYoungJun Cho }
241