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>
191c248b7dSInki Dae 
20e30655d0SMark Brown #include "exynos_drm_crtc.h"
211c248b7dSInki Dae #include "exynos_drm_drv.h"
22b5d2eb3bSJoonyoung Shim #include "exynos_drm_plane.h"
231c248b7dSInki Dae 
240b20a0f8SLaurent Pinchart static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc,
250b20a0f8SLaurent Pinchart 					  struct drm_crtc_state *old_state)
261c248b7dSInki Dae {
27d2716c89SJoonyoung Shim 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
281c248b7dSInki Dae 
293cecda03SGustavo Padovan 	if (exynos_crtc->ops->enable)
303cecda03SGustavo Padovan 		exynos_crtc->ops->enable(exynos_crtc);
31080be03dSSean Paul 
32d6948b2fSAndrzej Hajda 	drm_crtc_vblank_on(crtc);
331c248b7dSInki Dae }
341c248b7dSInki Dae 
353fc4867cSGustavo Padovan static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
363fc4867cSGustavo Padovan {
3763498e30SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
383fc4867cSGustavo Padovan 
3963498e30SGustavo Padovan 	drm_crtc_vblank_off(crtc);
4063498e30SGustavo Padovan 
413cecda03SGustavo Padovan 	if (exynos_crtc->ops->disable)
423cecda03SGustavo Padovan 		exynos_crtc->ops->disable(exynos_crtc);
4341cbf0fdSInki Dae 
4441cbf0fdSInki Dae 	if (crtc->state->event && !crtc->state->active) {
4541cbf0fdSInki Dae 		spin_lock_irq(&crtc->dev->event_lock);
4641cbf0fdSInki Dae 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
4741cbf0fdSInki Dae 		spin_unlock_irq(&crtc->dev->event_lock);
4841cbf0fdSInki Dae 
4941cbf0fdSInki Dae 		crtc->state->event = NULL;
5041cbf0fdSInki Dae 	}
513fc4867cSGustavo Padovan }
523fc4867cSGustavo Padovan 
535625b341SAndrzej Hajda static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
545625b341SAndrzej Hajda 				     struct drm_crtc_state *state)
555625b341SAndrzej Hajda {
565625b341SAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
575625b341SAndrzej Hajda 
58c4e07407SAndrzej Hajda 	if (!state->enable)
59c4e07407SAndrzej Hajda 		return 0;
60c4e07407SAndrzej Hajda 
615625b341SAndrzej Hajda 	if (exynos_crtc->ops->atomic_check)
625625b341SAndrzej Hajda 		return exynos_crtc->ops->atomic_check(exynos_crtc, state);
635625b341SAndrzej Hajda 
645625b341SAndrzej Hajda 	return 0;
655625b341SAndrzej Hajda }
665625b341SAndrzej Hajda 
67613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
68613d2b27SMaarten Lankhorst 				     struct drm_crtc_state *old_crtc_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,
77613d2b27SMaarten Lankhorst 				     struct drm_crtc_state *old_crtc_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 
85800ba2b5SVille Syrjälä static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
863fc4867cSGustavo Padovan 	.disable	= exynos_drm_crtc_disable,
875625b341SAndrzej Hajda 	.atomic_check	= exynos_crtc_atomic_check,
889d5ab6a0SGustavo Padovan 	.atomic_begin	= exynos_crtc_atomic_begin,
899d5ab6a0SGustavo Padovan 	.atomic_flush	= exynos_crtc_atomic_flush,
900b20a0f8SLaurent Pinchart 	.atomic_enable	= exynos_drm_crtc_atomic_enable,
911c248b7dSInki Dae };
921c248b7dSInki Dae 
93a392276dSAndrzej Hajda void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
94a392276dSAndrzej Hajda {
95a392276dSAndrzej Hajda 	struct drm_crtc *crtc = &exynos_crtc->base;
96a392276dSAndrzej Hajda 	struct drm_pending_vblank_event *event = crtc->state->event;
97a392276dSAndrzej Hajda 	unsigned long flags;
98a392276dSAndrzej Hajda 
9973b7b44fSAndrzej Hajda 	if (!event)
10073b7b44fSAndrzej Hajda 		return;
101a392276dSAndrzej Hajda 	crtc->state->event = NULL;
102a392276dSAndrzej Hajda 
10373b7b44fSAndrzej Hajda 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
10473b7b44fSAndrzej Hajda 
10573b7b44fSAndrzej Hajda 	spin_lock_irqsave(&crtc->dev->event_lock, flags);
10673b7b44fSAndrzej Hajda 	drm_crtc_arm_vblank_event(crtc, event);
10773b7b44fSAndrzej Hajda 	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
108a392276dSAndrzej Hajda }
109a392276dSAndrzej Hajda 
1101c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
1111c248b7dSInki Dae {
1121c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1131c248b7dSInki Dae 
1141c248b7dSInki Dae 	drm_crtc_cleanup(crtc);
1151c248b7dSInki Dae 	kfree(exynos_crtc);
1161c248b7dSInki Dae }
1171c248b7dSInki Dae 
11864b0e1d6SShawn Guo static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc)
11964b0e1d6SShawn Guo {
12064b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
12164b0e1d6SShawn Guo 
12264b0e1d6SShawn Guo 	if (exynos_crtc->ops->enable_vblank)
12364b0e1d6SShawn Guo 		return exynos_crtc->ops->enable_vblank(exynos_crtc);
12464b0e1d6SShawn Guo 
12564b0e1d6SShawn Guo 	return 0;
12664b0e1d6SShawn Guo }
12764b0e1d6SShawn Guo 
12864b0e1d6SShawn Guo static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
12964b0e1d6SShawn Guo {
13064b0e1d6SShawn Guo 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
13164b0e1d6SShawn Guo 
13264b0e1d6SShawn Guo 	if (exynos_crtc->ops->disable_vblank)
13364b0e1d6SShawn Guo 		exynos_crtc->ops->disable_vblank(exynos_crtc);
13464b0e1d6SShawn Guo }
13564b0e1d6SShawn Guo 
1360586febaSAndrzej Hajda static u32 exynos_drm_crtc_get_vblank_counter(struct drm_crtc *crtc)
1370586febaSAndrzej Hajda {
1380586febaSAndrzej Hajda 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1390586febaSAndrzej Hajda 
1400586febaSAndrzej Hajda 	if (exynos_crtc->ops->get_vblank_counter)
1410586febaSAndrzej Hajda 		return exynos_crtc->ops->get_vblank_counter(exynos_crtc);
1420586febaSAndrzej Hajda 
1430586febaSAndrzej Hajda 	return 0;
1440586febaSAndrzej Hajda }
1450586febaSAndrzej Hajda 
146800ba2b5SVille Syrjälä static const struct drm_crtc_funcs exynos_crtc_funcs = {
14747a7deffSGustavo Padovan 	.set_config	= drm_atomic_helper_set_config,
1489d5ab6a0SGustavo Padovan 	.page_flip	= drm_atomic_helper_page_flip,
1491c248b7dSInki Dae 	.destroy	= exynos_drm_crtc_destroy,
1504ea9526bSGustavo Padovan 	.reset = drm_atomic_helper_crtc_reset,
1514ea9526bSGustavo Padovan 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1524ea9526bSGustavo Padovan 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
15364b0e1d6SShawn Guo 	.enable_vblank = exynos_drm_crtc_enable_vblank,
15464b0e1d6SShawn Guo 	.disable_vblank = exynos_drm_crtc_disable_vblank,
1550586febaSAndrzej Hajda 	.get_vblank_counter = exynos_drm_crtc_get_vblank_counter,
1561c248b7dSInki Dae };
1571c248b7dSInki Dae 
15893bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
1597ee14cdcSGustavo Padovan 					struct drm_plane *plane,
16093bca243SGustavo Padovan 					enum exynos_drm_output_type type,
161f3aaf762SKrzysztof Kozlowski 					const struct exynos_drm_crtc_ops *ops,
16293bca243SGustavo Padovan 					void *ctx)
1631c248b7dSInki Dae {
1641c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc;
1651c248b7dSInki Dae 	struct drm_crtc *crtc;
16672ed6ccdSAndrzej Hajda 	int ret;
1671c248b7dSInki Dae 
1681c248b7dSInki Dae 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
16938bb5253SSachin Kamat 	if (!exynos_crtc)
17093bca243SGustavo Padovan 		return ERR_PTR(-ENOMEM);
1711c248b7dSInki Dae 
1725d1741adSGustavo Padovan 	exynos_crtc->type = type;
17393bca243SGustavo Padovan 	exynos_crtc->ops = ops;
17493bca243SGustavo Padovan 	exynos_crtc->ctx = ctx;
175b5d2eb3bSJoonyoung Shim 
176357193cdSGustavo Padovan 	crtc = &exynos_crtc->base;
1771c248b7dSInki Dae 
178eb88e422SGustavo Padovan 	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
179f9882876SVille Syrjälä 					&exynos_crtc_funcs, NULL);
18072ed6ccdSAndrzej Hajda 	if (ret < 0)
18172ed6ccdSAndrzej Hajda 		goto err_crtc;
18272ed6ccdSAndrzej Hajda 
1831c248b7dSInki Dae 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
1841c248b7dSInki Dae 
18593bca243SGustavo Padovan 	return exynos_crtc;
18672ed6ccdSAndrzej Hajda 
18772ed6ccdSAndrzej Hajda err_crtc:
18872ed6ccdSAndrzej Hajda 	plane->funcs->destroy(plane);
18972ed6ccdSAndrzej Hajda 	kfree(exynos_crtc);
19093bca243SGustavo Padovan 	return ERR_PTR(ret);
1911c248b7dSInki Dae }
1921c248b7dSInki Dae 
193f37cd5e8SInki Dae int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
194cf67cc9aSGustavo Padovan 				       enum exynos_drm_output_type out_type)
195f37cd5e8SInki Dae {
196f37cd5e8SInki Dae 	struct drm_crtc *crtc;
197f37cd5e8SInki Dae 
198d644951cSAndrzej Hajda 	drm_for_each_crtc(crtc, drm_dev)
199d644951cSAndrzej Hajda 		if (to_exynos_crtc(crtc)->type == out_type)
200d644951cSAndrzej Hajda 			return drm_crtc_index(crtc);
201f37cd5e8SInki Dae 
202f37cd5e8SInki Dae 	return -EPERM;
203f37cd5e8SInki Dae }
2045595d4d8SYoungJun Cho 
2055595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
2065595d4d8SYoungJun Cho {
20793bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
2085595d4d8SYoungJun Cho 
20993bca243SGustavo Padovan 	if (exynos_crtc->ops->te_handler)
21093bca243SGustavo Padovan 		exynos_crtc->ops->te_handler(exynos_crtc);
2115595d4d8SYoungJun Cho }
212