11c248b7dSInki Dae /* exynos_drm_crtc.c
21c248b7dSInki Dae  *
31c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
41c248b7dSInki Dae  * Authors:
51c248b7dSInki Dae  *	Inki Dae <inki.dae@samsung.com>
61c248b7dSInki Dae  *	Joonyoung Shim <jy0922.shim@samsung.com>
71c248b7dSInki Dae  *	Seung-Woo Kim <sw0312.kim@samsung.com>
81c248b7dSInki Dae  *
9d81aecb5SInki Dae  * This program is free software; you can redistribute  it and/or modify it
10d81aecb5SInki Dae  * under  the terms of  the GNU General  Public License as published by the
11d81aecb5SInki Dae  * Free Software Foundation;  either version 2 of the  License, or (at your
12d81aecb5SInki Dae  * option) any later version.
131c248b7dSInki Dae  */
141c248b7dSInki Dae 
15760285e7SDavid Howells #include <drm/drmP.h>
16760285e7SDavid Howells #include <drm/drm_crtc_helper.h>
174ea9526bSGustavo Padovan #include <drm/drm_atomic.h>
184ea9526bSGustavo Padovan #include <drm/drm_atomic_helper.h>
191ca582f1SAndrzej Hajda #include <drm/drm_encoder.h>
201c248b7dSInki Dae 
21e30655d0SMark Brown #include "exynos_drm_crtc.h"
221c248b7dSInki Dae #include "exynos_drm_drv.h"
23b5d2eb3bSJoonyoung Shim #include "exynos_drm_plane.h"
241c248b7dSInki Dae 
250b20a0f8SLaurent Pinchart static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc,
260b20a0f8SLaurent Pinchart 					  struct drm_crtc_state *old_state)
271c248b7dSInki Dae {
28d2716c89SJoonyoung Shim 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
291c248b7dSInki Dae 
303cecda03SGustavo Padovan 	if (exynos_crtc->ops->enable)
313cecda03SGustavo Padovan 		exynos_crtc->ops->enable(exynos_crtc);
32080be03dSSean Paul 
33d6948b2fSAndrzej Hajda 	drm_crtc_vblank_on(crtc);
341c248b7dSInki Dae }
351c248b7dSInki Dae 
3664581714SLaurent Pinchart static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc,
3764581714SLaurent Pinchart 					   struct drm_crtc_state *old_state)
383fc4867cSGustavo Padovan {
3963498e30SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
403fc4867cSGustavo Padovan 
4163498e30SGustavo Padovan 	drm_crtc_vblank_off(crtc);
4263498e30SGustavo Padovan 
433cecda03SGustavo Padovan 	if (exynos_crtc->ops->disable)
443cecda03SGustavo Padovan 		exynos_crtc->ops->disable(exynos_crtc);
4541cbf0fdSInki Dae 
4641cbf0fdSInki Dae 	if (crtc->state->event && !crtc->state->active) {
4741cbf0fdSInki Dae 		spin_lock_irq(&crtc->dev->event_lock);
4841cbf0fdSInki Dae 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
4941cbf0fdSInki Dae 		spin_unlock_irq(&crtc->dev->event_lock);
5041cbf0fdSInki Dae 
5141cbf0fdSInki Dae 		crtc->state->event = NULL;
5241cbf0fdSInki Dae 	}
533fc4867cSGustavo Padovan }
543fc4867cSGustavo Padovan 
555625b341SAndrzej Hajda static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
565625b341SAndrzej Hajda 				     struct drm_crtc_state *state)
575625b341SAndrzej Hajda {
585625b341SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
595625b341SAndrzej Hajda 
60c4e07407SAndrzej Hajda 	if (!state->enable)
61c4e07407SAndrzej Hajda 		return 0;
62c4e07407SAndrzej Hajda 
635625b341SAndrzej Hajda 	if (exynos_crtc->ops->atomic_check)
645625b341SAndrzej Hajda 		return exynos_crtc->ops->atomic_check(exynos_crtc, state);
655625b341SAndrzej Hajda 
665625b341SAndrzej Hajda 	return 0;
675625b341SAndrzej Hajda }
685625b341SAndrzej Hajda 
69613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
70613d2b27SMaarten Lankhorst 				     struct drm_crtc_state *old_crtc_state)
719d5ab6a0SGustavo Padovan {
729d5ab6a0SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
739d5ab6a0SGustavo Padovan 
74d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_begin)
75d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_begin(exynos_crtc);
769d5ab6a0SGustavo Padovan }
779d5ab6a0SGustavo Padovan 
78613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
79613d2b27SMaarten Lankhorst 				     struct drm_crtc_state *old_crtc_state)
809d5ab6a0SGustavo Padovan {
81d9220d47SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
82d9220d47SGustavo Padovan 
83d9220d47SGustavo Padovan 	if (exynos_crtc->ops->atomic_flush)
84d29c2c14SMarek Szyprowski 		exynos_crtc->ops->atomic_flush(exynos_crtc);
859d5ab6a0SGustavo Padovan }
869d5ab6a0SGustavo Padovan 
87c3653fedSAndrzej Hajda static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
88c3653fedSAndrzej Hajda 	const struct drm_display_mode *mode)
89c3653fedSAndrzej Hajda {
90c3653fedSAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
91c3653fedSAndrzej Hajda 
92c3653fedSAndrzej Hajda 	if (exynos_crtc->ops->mode_valid)
93c3653fedSAndrzej Hajda 		return exynos_crtc->ops->mode_valid(exynos_crtc, mode);
94c3653fedSAndrzej Hajda 
95c3653fedSAndrzej Hajda 	return MODE_OK;
96c3653fedSAndrzej Hajda }
97c3653fedSAndrzej Hajda 
982466db97SAndrzej Hajda static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc,
992466db97SAndrzej Hajda 		const struct drm_display_mode *mode,
1002466db97SAndrzej Hajda 		struct drm_display_mode *adjusted_mode)
1012466db97SAndrzej Hajda {
1022466db97SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1032466db97SAndrzej Hajda 
1042466db97SAndrzej Hajda 	if (exynos_crtc->ops->mode_fixup)
1052466db97SAndrzej Hajda 		return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
1062466db97SAndrzej Hajda 				adjusted_mode);
1072466db97SAndrzej Hajda 
1082466db97SAndrzej Hajda 	return true;
1092466db97SAndrzej Hajda }
1102466db97SAndrzej Hajda 
1112466db97SAndrzej Hajda 
112800ba2b5SVille Syrjälä static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
113c3653fedSAndrzej Hajda 	.mode_valid	= exynos_crtc_mode_valid,
1142466db97SAndrzej Hajda 	.mode_fixup	= exynos_crtc_mode_fixup,
1155625b341SAndrzej Hajda 	.atomic_check	= exynos_crtc_atomic_check,
1169d5ab6a0SGustavo Padovan 	.atomic_begin	= exynos_crtc_atomic_begin,
1179d5ab6a0SGustavo Padovan 	.atomic_flush	= exynos_crtc_atomic_flush,
1180b20a0f8SLaurent Pinchart 	.atomic_enable	= exynos_drm_crtc_atomic_enable,
11964581714SLaurent Pinchart 	.atomic_disable	= exynos_drm_crtc_atomic_disable,
1201c248b7dSInki Dae };
1211c248b7dSInki Dae 
122a392276dSAndrzej Hajda void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
123a392276dSAndrzej Hajda {
124a392276dSAndrzej Hajda 	struct drm_crtc *crtc = &exynos_crtc->base;
125a392276dSAndrzej Hajda 	struct drm_pending_vblank_event *event = crtc->state->event;
126a392276dSAndrzej Hajda 	unsigned long flags;
127a392276dSAndrzej Hajda 
12873b7b44fSAndrzej Hajda 	if (!event)
12973b7b44fSAndrzej Hajda 		return;
130a392276dSAndrzej Hajda 	crtc->state->event = NULL;
131a392276dSAndrzej Hajda 
13273b7b44fSAndrzej Hajda 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
13373b7b44fSAndrzej Hajda 
13473b7b44fSAndrzej Hajda 	spin_lock_irqsave(&crtc->dev->event_lock, flags);
13573b7b44fSAndrzej Hajda 	drm_crtc_arm_vblank_event(crtc, event);
13673b7b44fSAndrzej Hajda 	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
137a392276dSAndrzej Hajda }
138a392276dSAndrzej Hajda 
1391c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
1401c248b7dSInki Dae {
1411c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1421c248b7dSInki Dae 
1431c248b7dSInki Dae 	drm_crtc_cleanup(crtc);
1441c248b7dSInki Dae 	kfree(exynos_crtc);
1451c248b7dSInki Dae }
1461c248b7dSInki Dae 
14764b0e1d6SShawn Guo static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc)
14864b0e1d6SShawn Guo {
14964b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
15064b0e1d6SShawn Guo 
15164b0e1d6SShawn Guo 	if (exynos_crtc->ops->enable_vblank)
15264b0e1d6SShawn Guo 		return exynos_crtc->ops->enable_vblank(exynos_crtc);
15364b0e1d6SShawn Guo 
15464b0e1d6SShawn Guo 	return 0;
15564b0e1d6SShawn Guo }
15664b0e1d6SShawn Guo 
15764b0e1d6SShawn Guo static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
15864b0e1d6SShawn Guo {
15964b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
16064b0e1d6SShawn Guo 
16164b0e1d6SShawn Guo 	if (exynos_crtc->ops->disable_vblank)
16264b0e1d6SShawn Guo 		exynos_crtc->ops->disable_vblank(exynos_crtc);
16364b0e1d6SShawn Guo }
16464b0e1d6SShawn Guo 
1650586febaSAndrzej Hajda static u32 exynos_drm_crtc_get_vblank_counter(struct drm_crtc *crtc)
1660586febaSAndrzej Hajda {
1670586febaSAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1680586febaSAndrzej Hajda 
1690586febaSAndrzej Hajda 	if (exynos_crtc->ops->get_vblank_counter)
1700586febaSAndrzej Hajda 		return exynos_crtc->ops->get_vblank_counter(exynos_crtc);
1710586febaSAndrzej Hajda 
1720586febaSAndrzej Hajda 	return 0;
1730586febaSAndrzej Hajda }
1740586febaSAndrzej Hajda 
175800ba2b5SVille Syrjälä static const struct drm_crtc_funcs exynos_crtc_funcs = {
17647a7deffSGustavo Padovan 	.set_config	= drm_atomic_helper_set_config,
1779d5ab6a0SGustavo Padovan 	.page_flip	= drm_atomic_helper_page_flip,
1781c248b7dSInki Dae 	.destroy	= exynos_drm_crtc_destroy,
1794ea9526bSGustavo Padovan 	.reset = drm_atomic_helper_crtc_reset,
1804ea9526bSGustavo Padovan 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1814ea9526bSGustavo Padovan 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
18264b0e1d6SShawn Guo 	.enable_vblank = exynos_drm_crtc_enable_vblank,
18364b0e1d6SShawn Guo 	.disable_vblank = exynos_drm_crtc_disable_vblank,
1840586febaSAndrzej Hajda 	.get_vblank_counter = exynos_drm_crtc_get_vblank_counter,
1851c248b7dSInki Dae };
1861c248b7dSInki Dae 
18793bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
1887ee14cdcSGustavo Padovan 					struct drm_plane *plane,
18993bca243SGustavo Padovan 					enum exynos_drm_output_type type,
190f3aaf762SKrzysztof Kozlowski 					const struct exynos_drm_crtc_ops *ops,
19193bca243SGustavo Padovan 					void *ctx)
1921c248b7dSInki Dae {
1931c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc;
1941c248b7dSInki Dae 	struct drm_crtc *crtc;
19572ed6ccdSAndrzej Hajda 	int ret;
1961c248b7dSInki Dae 
1971c248b7dSInki Dae 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
19838bb5253SSachin Kamat 	if (!exynos_crtc)
19993bca243SGustavo Padovan 		return ERR_PTR(-ENOMEM);
2001c248b7dSInki Dae 
2015d1741adSGustavo Padovan 	exynos_crtc->type = type;
20293bca243SGustavo Padovan 	exynos_crtc->ops = ops;
20393bca243SGustavo Padovan 	exynos_crtc->ctx = ctx;
204b5d2eb3bSJoonyoung Shim 
205357193cdSGustavo Padovan 	crtc = &exynos_crtc->base;
2061c248b7dSInki Dae 
207eb88e422SGustavo Padovan 	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
208f9882876SVille Syrjälä 					&exynos_crtc_funcs, NULL);
20972ed6ccdSAndrzej Hajda 	if (ret < 0)
21072ed6ccdSAndrzej Hajda 		goto err_crtc;
21172ed6ccdSAndrzej Hajda 
2121c248b7dSInki Dae 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
2131c248b7dSInki Dae 
21493bca243SGustavo Padovan 	return exynos_crtc;
21572ed6ccdSAndrzej Hajda 
21672ed6ccdSAndrzej Hajda err_crtc:
21772ed6ccdSAndrzej Hajda 	plane->funcs->destroy(plane);
21872ed6ccdSAndrzej Hajda 	kfree(exynos_crtc);
21993bca243SGustavo Padovan 	return ERR_PTR(ret);
2201c248b7dSInki Dae }
2211c248b7dSInki Dae 
2221ca582f1SAndrzej Hajda struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
223cf67cc9aSGustavo Padovan 				       enum exynos_drm_output_type out_type)
224f37cd5e8SInki Dae {
225f37cd5e8SInki Dae 	struct drm_crtc *crtc;
226f37cd5e8SInki Dae 
227d644951cSAndrzej Hajda 	drm_for_each_crtc(crtc, drm_dev)
228d644951cSAndrzej Hajda 		if (to_exynos_crtc(crtc)->type == out_type)
2291ca582f1SAndrzej Hajda 			return to_exynos_crtc(crtc);
230f37cd5e8SInki Dae 
231e9497dc2SMarek Szyprowski 	return ERR_PTR(-ENODEV);
2321ca582f1SAndrzej Hajda }
2331ca582f1SAndrzej Hajda 
2341ca582f1SAndrzej Hajda int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
2351ca582f1SAndrzej Hajda 		enum exynos_drm_output_type out_type)
2361ca582f1SAndrzej Hajda {
2371ca582f1SAndrzej Hajda 	struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev,
2381ca582f1SAndrzej Hajda 						out_type);
2391ca582f1SAndrzej Hajda 
2401ca582f1SAndrzej Hajda 	if (IS_ERR(crtc))
2411ca582f1SAndrzej Hajda 		return PTR_ERR(crtc);
2421ca582f1SAndrzej Hajda 
2431ca582f1SAndrzej Hajda 	encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
2441ca582f1SAndrzej Hajda 
2451ca582f1SAndrzej Hajda 	return 0;
246f37cd5e8SInki Dae }
2475595d4d8SYoungJun Cho 
2485595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
2495595d4d8SYoungJun Cho {
25093bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
2515595d4d8SYoungJun Cho 
25293bca243SGustavo Padovan 	if (exynos_crtc->ops->te_handler)
25393bca243SGustavo Padovan 		exynos_crtc->ops->te_handler(exynos_crtc);
2545595d4d8SYoungJun Cho }
255