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 
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 
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 	if (crtc->state->event && !crtc->state->active) {
4341cbf0fdSInki Dae 		spin_lock_irq(&crtc->dev->event_lock);
4441cbf0fdSInki Dae 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
4541cbf0fdSInki Dae 		spin_unlock_irq(&crtc->dev->event_lock);
4641cbf0fdSInki Dae 
4741cbf0fdSInki Dae 		crtc->state->event = NULL;
4841cbf0fdSInki Dae 	}
493fc4867cSGustavo Padovan }
503fc4867cSGustavo Padovan 
515625b341SAndrzej Hajda static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
5229b77ad7SMaxime Ripard 				     struct drm_atomic_state *state)
535625b341SAndrzej Hajda {
5429b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
5529b77ad7SMaxime Ripard 									  crtc);
565625b341SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
575625b341SAndrzej Hajda 
5829b77ad7SMaxime Ripard 	if (!crtc_state->enable)
59c4e07407SAndrzej Hajda 		return 0;
60c4e07407SAndrzej Hajda 
615625b341SAndrzej Hajda 	if (exynos_crtc->ops->atomic_check)
6229b77ad7SMaxime Ripard 		return exynos_crtc->ops->atomic_check(exynos_crtc, crtc_state);
635625b341SAndrzej Hajda 
645625b341SAndrzej Hajda 	return 0;
655625b341SAndrzej Hajda }
665625b341SAndrzej Hajda 
67613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
68*f6ebe9f9SMaxime Ripard 				     struct drm_atomic_state *state)
699d5ab6a0SGustavo Padovan {
709d5ab6a0SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
719d5ab6a0SGustavo Padovan 
72d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_begin)
73d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_begin(exynos_crtc);
749d5ab6a0SGustavo Padovan }
759d5ab6a0SGustavo Padovan 
76613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
77*f6ebe9f9SMaxime Ripard 				     struct drm_atomic_state *state)
789d5ab6a0SGustavo Padovan {
79d9220d47SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
80d9220d47SGustavo Padovan 
81d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_flush)
82d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_flush(exynos_crtc);
839d5ab6a0SGustavo Padovan }
849d5ab6a0SGustavo Padovan 
85c3653fedSAndrzej Hajda static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
86c3653fedSAndrzej Hajda 	const struct drm_display_mode *mode)
87c3653fedSAndrzej Hajda {
88c3653fedSAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
89c3653fedSAndrzej Hajda 
90c3653fedSAndrzej Hajda 	if (exynos_crtc->ops->mode_valid)
91c3653fedSAndrzej Hajda 		return exynos_crtc->ops->mode_valid(exynos_crtc, mode);
92c3653fedSAndrzej Hajda 
93c3653fedSAndrzej Hajda 	return MODE_OK;
94c3653fedSAndrzej Hajda }
95c3653fedSAndrzej Hajda 
962466db97SAndrzej Hajda static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc,
972466db97SAndrzej Hajda 		const struct drm_display_mode *mode,
982466db97SAndrzej Hajda 		struct drm_display_mode *adjusted_mode)
992466db97SAndrzej Hajda {
1002466db97SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1012466db97SAndrzej Hajda 
1022466db97SAndrzej Hajda 	if (exynos_crtc->ops->mode_fixup)
1032466db97SAndrzej Hajda 		return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
1042466db97SAndrzej Hajda 				adjusted_mode);
1052466db97SAndrzej Hajda 
1062466db97SAndrzej Hajda 	return true;
1072466db97SAndrzej Hajda }
1082466db97SAndrzej Hajda 
1092466db97SAndrzej Hajda 
110800ba2b5SVille Syrjälä static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
111c3653fedSAndrzej Hajda 	.mode_valid	= exynos_crtc_mode_valid,
1122466db97SAndrzej Hajda 	.mode_fixup	= exynos_crtc_mode_fixup,
1135625b341SAndrzej Hajda 	.atomic_check	= exynos_crtc_atomic_check,
1149d5ab6a0SGustavo Padovan 	.atomic_begin	= exynos_crtc_atomic_begin,
1159d5ab6a0SGustavo Padovan 	.atomic_flush	= exynos_crtc_atomic_flush,
1160b20a0f8SLaurent Pinchart 	.atomic_enable	= exynos_drm_crtc_atomic_enable,
11764581714SLaurent Pinchart 	.atomic_disable	= exynos_drm_crtc_atomic_disable,
1181c248b7dSInki Dae };
1191c248b7dSInki Dae 
120a392276dSAndrzej Hajda void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
121a392276dSAndrzej Hajda {
122a392276dSAndrzej Hajda 	struct drm_crtc *crtc = &exynos_crtc->base;
123a392276dSAndrzej Hajda 	struct drm_pending_vblank_event *event = crtc->state->event;
124a392276dSAndrzej Hajda 	unsigned long flags;
125a392276dSAndrzej Hajda 
12673b7b44fSAndrzej Hajda 	if (!event)
12773b7b44fSAndrzej Hajda 		return;
128a392276dSAndrzej Hajda 	crtc->state->event = NULL;
129a392276dSAndrzej Hajda 
13073b7b44fSAndrzej Hajda 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
13173b7b44fSAndrzej Hajda 
13273b7b44fSAndrzej Hajda 	spin_lock_irqsave(&crtc->dev->event_lock, flags);
13373b7b44fSAndrzej Hajda 	drm_crtc_arm_vblank_event(crtc, event);
13473b7b44fSAndrzej Hajda 	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
135a392276dSAndrzej Hajda }
136a392276dSAndrzej Hajda 
1371c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
1381c248b7dSInki Dae {
1391c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1401c248b7dSInki Dae 
1411c248b7dSInki Dae 	drm_crtc_cleanup(crtc);
1421c248b7dSInki Dae 	kfree(exynos_crtc);
1431c248b7dSInki Dae }
1441c248b7dSInki Dae 
14564b0e1d6SShawn Guo static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc)
14664b0e1d6SShawn Guo {
14764b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
14864b0e1d6SShawn Guo 
14964b0e1d6SShawn Guo 	if (exynos_crtc->ops->enable_vblank)
15064b0e1d6SShawn Guo 		return exynos_crtc->ops->enable_vblank(exynos_crtc);
15164b0e1d6SShawn Guo 
15264b0e1d6SShawn Guo 	return 0;
15364b0e1d6SShawn Guo }
15464b0e1d6SShawn Guo 
15564b0e1d6SShawn Guo static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
15664b0e1d6SShawn Guo {
15764b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
15864b0e1d6SShawn Guo 
15964b0e1d6SShawn Guo 	if (exynos_crtc->ops->disable_vblank)
16064b0e1d6SShawn Guo 		exynos_crtc->ops->disable_vblank(exynos_crtc);
16164b0e1d6SShawn Guo }
16264b0e1d6SShawn Guo 
163800ba2b5SVille Syrjälä static const struct drm_crtc_funcs exynos_crtc_funcs = {
16447a7deffSGustavo Padovan 	.set_config	= drm_atomic_helper_set_config,
1659d5ab6a0SGustavo Padovan 	.page_flip	= drm_atomic_helper_page_flip,
1661c248b7dSInki Dae 	.destroy	= exynos_drm_crtc_destroy,
1674ea9526bSGustavo Padovan 	.reset = drm_atomic_helper_crtc_reset,
1684ea9526bSGustavo Padovan 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1694ea9526bSGustavo Padovan 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
17064b0e1d6SShawn Guo 	.enable_vblank = exynos_drm_crtc_enable_vblank,
17164b0e1d6SShawn Guo 	.disable_vblank = exynos_drm_crtc_disable_vblank,
1721c248b7dSInki Dae };
1731c248b7dSInki Dae 
17493bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
1757ee14cdcSGustavo Padovan 					struct drm_plane *plane,
17693bca243SGustavo Padovan 					enum exynos_drm_output_type type,
177f3aaf762SKrzysztof Kozlowski 					const struct exynos_drm_crtc_ops *ops,
17893bca243SGustavo Padovan 					void *ctx)
1791c248b7dSInki Dae {
1801c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc;
1811c248b7dSInki Dae 	struct drm_crtc *crtc;
18272ed6ccdSAndrzej Hajda 	int ret;
1831c248b7dSInki Dae 
1841c248b7dSInki Dae 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
18538bb5253SSachin Kamat 	if (!exynos_crtc)
18693bca243SGustavo Padovan 		return ERR_PTR(-ENOMEM);
1871c248b7dSInki Dae 
1885d1741adSGustavo Padovan 	exynos_crtc->type = type;
18993bca243SGustavo Padovan 	exynos_crtc->ops = ops;
19093bca243SGustavo Padovan 	exynos_crtc->ctx = ctx;
191b5d2eb3bSJoonyoung Shim 
192357193cdSGustavo Padovan 	crtc = &exynos_crtc->base;
1931c248b7dSInki Dae 
194eb88e422SGustavo Padovan 	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
195f9882876SVille Syrjälä 					&exynos_crtc_funcs, NULL);
19672ed6ccdSAndrzej Hajda 	if (ret < 0)
19772ed6ccdSAndrzej Hajda 		goto err_crtc;
19872ed6ccdSAndrzej Hajda 
1991c248b7dSInki Dae 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
2001c248b7dSInki Dae 
20193bca243SGustavo Padovan 	return exynos_crtc;
20272ed6ccdSAndrzej Hajda 
20372ed6ccdSAndrzej Hajda err_crtc:
20472ed6ccdSAndrzej Hajda 	plane->funcs->destroy(plane);
20572ed6ccdSAndrzej Hajda 	kfree(exynos_crtc);
20693bca243SGustavo Padovan 	return ERR_PTR(ret);
2071c248b7dSInki Dae }
2081c248b7dSInki Dae 
2091ca582f1SAndrzej Hajda struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
210cf67cc9aSGustavo Padovan 				       enum exynos_drm_output_type out_type)
211f37cd5e8SInki Dae {
212f37cd5e8SInki Dae 	struct drm_crtc *crtc;
213f37cd5e8SInki Dae 
214d644951cSAndrzej Hajda 	drm_for_each_crtc(crtc, drm_dev)
215d644951cSAndrzej Hajda 		if (to_exynos_crtc(crtc)->type == out_type)
2161ca582f1SAndrzej Hajda 			return to_exynos_crtc(crtc);
217f37cd5e8SInki Dae 
218e9497dc2SMarek Szyprowski 	return ERR_PTR(-ENODEV);
2191ca582f1SAndrzej Hajda }
2201ca582f1SAndrzej Hajda 
2211ca582f1SAndrzej Hajda int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
2221ca582f1SAndrzej Hajda 		enum exynos_drm_output_type out_type)
2231ca582f1SAndrzej Hajda {
2241ca582f1SAndrzej Hajda 	struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev,
2251ca582f1SAndrzej Hajda 						out_type);
2261ca582f1SAndrzej Hajda 
2271ca582f1SAndrzej Hajda 	if (IS_ERR(crtc))
2281ca582f1SAndrzej Hajda 		return PTR_ERR(crtc);
2291ca582f1SAndrzej Hajda 
2301ca582f1SAndrzej Hajda 	encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
2311ca582f1SAndrzej Hajda 
2321ca582f1SAndrzej Hajda 	return 0;
233f37cd5e8SInki Dae }
2345595d4d8SYoungJun Cho 
2355595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
2365595d4d8SYoungJun Cho {
23793bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
2385595d4d8SYoungJun Cho 
23993bca243SGustavo Padovan 	if (exynos_crtc->ops->te_handler)
24093bca243SGustavo Padovan 		exynos_crtc->ops->te_handler(exynos_crtc);
2415595d4d8SYoungJun Cho }
242