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"
221c248b7dSInki Dae #include "exynos_drm_encoder.h"
23b5d2eb3bSJoonyoung Shim #include "exynos_drm_plane.h"
241c248b7dSInki Dae 
2563498e30SGustavo Padovan static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
261c248b7dSInki Dae {
27d2716c89SJoonyoung Shim 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
281c248b7dSInki Dae 
2963498e30SGustavo Padovan 	if (exynos_crtc->enabled)
30ec05da95SInki Dae 		return;
3120cd2640SInki Dae 
323cecda03SGustavo Padovan 	if (exynos_crtc->ops->enable)
333cecda03SGustavo Padovan 		exynos_crtc->ops->enable(exynos_crtc);
34080be03dSSean Paul 
3563498e30SGustavo Padovan 	exynos_crtc->enabled = true;
36d6948b2fSAndrzej Hajda 
37d6948b2fSAndrzej Hajda 	drm_crtc_vblank_on(crtc);
381c248b7dSInki Dae }
391c248b7dSInki Dae 
403fc4867cSGustavo Padovan static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
413fc4867cSGustavo Padovan {
4263498e30SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
433fc4867cSGustavo Padovan 
4463498e30SGustavo Padovan 	if (!exynos_crtc->enabled)
4563498e30SGustavo Padovan 		return;
4663498e30SGustavo Padovan 
4763498e30SGustavo Padovan 	/* wait for the completion of page flip. */
4863498e30SGustavo Padovan 	if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
4963498e30SGustavo Padovan 				(exynos_crtc->event == NULL), HZ/20))
5063498e30SGustavo Padovan 		exynos_crtc->event = NULL;
5163498e30SGustavo Padovan 
5263498e30SGustavo Padovan 	drm_crtc_vblank_off(crtc);
5363498e30SGustavo Padovan 
543cecda03SGustavo Padovan 	if (exynos_crtc->ops->disable)
553cecda03SGustavo Padovan 		exynos_crtc->ops->disable(exynos_crtc);
5663498e30SGustavo Padovan 
5763498e30SGustavo Padovan 	exynos_crtc->enabled = false;
583fc4867cSGustavo Padovan }
593fc4867cSGustavo Padovan 
601c248b7dSInki Dae static bool
611c248b7dSInki Dae exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
62e811f5aeSLaurent Pinchart 			    const struct drm_display_mode *mode,
631c248b7dSInki Dae 			    struct drm_display_mode *adjusted_mode)
641c248b7dSInki Dae {
654b405269SSean Paul 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
664b405269SSean Paul 
6793bca243SGustavo Padovan 	if (exynos_crtc->ops->mode_fixup)
6893bca243SGustavo Padovan 		return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
6993bca243SGustavo Padovan 						    adjusted_mode);
704b405269SSean Paul 
711c248b7dSInki Dae 	return true;
721c248b7dSInki Dae }
731c248b7dSInki Dae 
74199329cbSGustavo Padovan static void
75199329cbSGustavo Padovan exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
761c248b7dSInki Dae {
774070d212SJoonyoung Shim 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
781c248b7dSInki Dae 
79199329cbSGustavo Padovan 	if (exynos_crtc->ops->commit)
80199329cbSGustavo Padovan 		exynos_crtc->ops->commit(exynos_crtc);
811c248b7dSInki Dae }
821c248b7dSInki Dae 
839d5ab6a0SGustavo Padovan static void exynos_crtc_atomic_begin(struct drm_crtc *crtc)
849d5ab6a0SGustavo Padovan {
859d5ab6a0SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
869d5ab6a0SGustavo Padovan 
879d5ab6a0SGustavo Padovan 	if (crtc->state->event) {
889d5ab6a0SGustavo Padovan 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
899d5ab6a0SGustavo Padovan 		exynos_crtc->event = crtc->state->event;
909d5ab6a0SGustavo Padovan 	}
919d5ab6a0SGustavo Padovan }
929d5ab6a0SGustavo Padovan 
939d5ab6a0SGustavo Padovan static void exynos_crtc_atomic_flush(struct drm_crtc *crtc)
949d5ab6a0SGustavo Padovan {
959d5ab6a0SGustavo Padovan }
969d5ab6a0SGustavo Padovan 
971c248b7dSInki Dae static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
9863498e30SGustavo Padovan 	.enable		= exynos_drm_crtc_enable,
993fc4867cSGustavo Padovan 	.disable	= exynos_drm_crtc_disable,
1001c248b7dSInki Dae 	.mode_fixup	= exynos_drm_crtc_mode_fixup,
101199329cbSGustavo Padovan 	.mode_set_nofb	= exynos_drm_crtc_mode_set_nofb,
1029d5ab6a0SGustavo Padovan 	.atomic_begin	= exynos_crtc_atomic_begin,
1039d5ab6a0SGustavo Padovan 	.atomic_flush	= exynos_crtc_atomic_flush,
1041c248b7dSInki Dae };
1051c248b7dSInki Dae 
1061c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
1071c248b7dSInki Dae {
1081c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
1091c248b7dSInki Dae 	struct exynos_drm_private *private = crtc->dev->dev_private;
1101c248b7dSInki Dae 
1111c248b7dSInki Dae 	private->crtc[exynos_crtc->pipe] = NULL;
1121c248b7dSInki Dae 
1131c248b7dSInki Dae 	drm_crtc_cleanup(crtc);
1141c248b7dSInki Dae 	kfree(exynos_crtc);
1151c248b7dSInki Dae }
1161c248b7dSInki Dae 
1171c248b7dSInki Dae static struct drm_crtc_funcs exynos_crtc_funcs = {
11847a7deffSGustavo Padovan 	.set_config	= drm_atomic_helper_set_config,
1199d5ab6a0SGustavo Padovan 	.page_flip	= drm_atomic_helper_page_flip,
1201c248b7dSInki Dae 	.destroy	= exynos_drm_crtc_destroy,
1214ea9526bSGustavo Padovan 	.reset = drm_atomic_helper_crtc_reset,
1224ea9526bSGustavo Padovan 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1234ea9526bSGustavo Padovan 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
1241c248b7dSInki Dae };
1251c248b7dSInki Dae 
12693bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
1277ee14cdcSGustavo Padovan 					struct drm_plane *plane,
12893bca243SGustavo Padovan 					int pipe,
12993bca243SGustavo Padovan 					enum exynos_drm_output_type type,
130f3aaf762SKrzysztof Kozlowski 					const struct exynos_drm_crtc_ops *ops,
13193bca243SGustavo Padovan 					void *ctx)
1321c248b7dSInki Dae {
1331c248b7dSInki Dae 	struct exynos_drm_crtc *exynos_crtc;
134eb88e422SGustavo Padovan 	struct exynos_drm_private *private = drm_dev->dev_private;
1351c248b7dSInki Dae 	struct drm_crtc *crtc;
13672ed6ccdSAndrzej Hajda 	int ret;
1371c248b7dSInki Dae 
1381c248b7dSInki Dae 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
13938bb5253SSachin Kamat 	if (!exynos_crtc)
14093bca243SGustavo Padovan 		return ERR_PTR(-ENOMEM);
1411c248b7dSInki Dae 
14220cd2640SInki Dae 	init_waitqueue_head(&exynos_crtc->pending_flip_queue);
143080be03dSSean Paul 
144e09f2b0dSGustavo Padovan 	exynos_crtc->pipe = pipe;
1455d1741adSGustavo Padovan 	exynos_crtc->type = type;
14693bca243SGustavo Padovan 	exynos_crtc->ops = ops;
14793bca243SGustavo Padovan 	exynos_crtc->ctx = ctx;
148b5d2eb3bSJoonyoung Shim 
149357193cdSGustavo Padovan 	crtc = &exynos_crtc->base;
1501c248b7dSInki Dae 
151e09f2b0dSGustavo Padovan 	private->crtc[pipe] = crtc;
1521c248b7dSInki Dae 
153eb88e422SGustavo Padovan 	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
15472ed6ccdSAndrzej Hajda 					&exynos_crtc_funcs);
15572ed6ccdSAndrzej Hajda 	if (ret < 0)
15672ed6ccdSAndrzej Hajda 		goto err_crtc;
15772ed6ccdSAndrzej Hajda 
1581c248b7dSInki Dae 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
1591c248b7dSInki Dae 
16093bca243SGustavo Padovan 	return exynos_crtc;
16172ed6ccdSAndrzej Hajda 
16272ed6ccdSAndrzej Hajda err_crtc:
16372ed6ccdSAndrzej Hajda 	plane->funcs->destroy(plane);
16472ed6ccdSAndrzej Hajda 	kfree(exynos_crtc);
16593bca243SGustavo Padovan 	return ERR_PTR(ret);
1661c248b7dSInki Dae }
1671c248b7dSInki Dae 
168080be03dSSean Paul int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
1691c248b7dSInki Dae {
1701c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
171ec05da95SInki Dae 	struct exynos_drm_crtc *exynos_crtc =
172080be03dSSean Paul 		to_exynos_crtc(private->crtc[pipe]);
1731c248b7dSInki Dae 
17463498e30SGustavo Padovan 	if (!exynos_crtc->enabled)
175ec05da95SInki Dae 		return -EPERM;
176ec05da95SInki Dae 
17793bca243SGustavo Padovan 	if (exynos_crtc->ops->enable_vblank)
17893bca243SGustavo Padovan 		exynos_crtc->ops->enable_vblank(exynos_crtc);
1791c248b7dSInki Dae 
1801c248b7dSInki Dae 	return 0;
1811c248b7dSInki Dae }
1821c248b7dSInki Dae 
183080be03dSSean Paul void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
1841c248b7dSInki Dae {
1851c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
186ec05da95SInki Dae 	struct exynos_drm_crtc *exynos_crtc =
187080be03dSSean Paul 		to_exynos_crtc(private->crtc[pipe]);
1881c248b7dSInki Dae 
18963498e30SGustavo Padovan 	if (!exynos_crtc->enabled)
190ec05da95SInki Dae 		return;
191ec05da95SInki Dae 
19293bca243SGustavo Padovan 	if (exynos_crtc->ops->disable_vblank)
19393bca243SGustavo Padovan 		exynos_crtc->ops->disable_vblank(exynos_crtc);
1941c248b7dSInki Dae }
195663d8766SRahul Sharma 
196080be03dSSean Paul void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
197663d8766SRahul Sharma {
198663d8766SRahul Sharma 	struct exynos_drm_private *dev_priv = dev->dev_private;
199080be03dSSean Paul 	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
20020cd2640SInki Dae 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
201663d8766SRahul Sharma 	unsigned long flags;
202663d8766SRahul Sharma 
203663d8766SRahul Sharma 	spin_lock_irqsave(&dev->event_lock, flags);
204e752747bSMandeep Singh Baines 	if (exynos_crtc->event) {
205663d8766SRahul Sharma 
206e752747bSMandeep Singh Baines 		drm_send_vblank_event(dev, -1, exynos_crtc->event);
207080be03dSSean Paul 		drm_vblank_put(dev, pipe);
20820cd2640SInki Dae 		wake_up(&exynos_crtc->pending_flip_queue);
209e752747bSMandeep Singh Baines 
210663d8766SRahul Sharma 	}
211663d8766SRahul Sharma 
212e752747bSMandeep Singh Baines 	exynos_crtc->event = NULL;
213663d8766SRahul Sharma 	spin_unlock_irqrestore(&dev->event_lock, flags);
214663d8766SRahul Sharma }
215080be03dSSean Paul 
216080be03dSSean Paul void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
217080be03dSSean Paul {
21893bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc;
219080be03dSSean Paul 	struct drm_device *dev = fb->dev;
220080be03dSSean Paul 	struct drm_crtc *crtc;
221080be03dSSean Paul 
222080be03dSSean Paul 	/*
223080be03dSSean Paul 	 * make sure that overlay data are updated to real hardware
224080be03dSSean Paul 	 * for all encoders.
225080be03dSSean Paul 	 */
226080be03dSSean Paul 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
22793bca243SGustavo Padovan 		exynos_crtc = to_exynos_crtc(crtc);
228080be03dSSean Paul 
229080be03dSSean Paul 		/*
230080be03dSSean Paul 		 * wait for vblank interrupt
231080be03dSSean Paul 		 * - this makes sure that overlay data are updated to
232080be03dSSean Paul 		 *	real hardware.
233080be03dSSean Paul 		 */
23493bca243SGustavo Padovan 		if (exynos_crtc->ops->wait_for_vblank)
23593bca243SGustavo Padovan 			exynos_crtc->ops->wait_for_vblank(exynos_crtc);
236080be03dSSean Paul 	}
237080be03dSSean Paul }
238f37cd5e8SInki Dae 
239f37cd5e8SInki Dae int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
240f37cd5e8SInki Dae 					unsigned int out_type)
241f37cd5e8SInki Dae {
242f37cd5e8SInki Dae 	struct drm_crtc *crtc;
243f37cd5e8SInki Dae 
244f37cd5e8SInki Dae 	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
245f37cd5e8SInki Dae 		struct exynos_drm_crtc *exynos_crtc;
246f37cd5e8SInki Dae 
247f37cd5e8SInki Dae 		exynos_crtc = to_exynos_crtc(crtc);
2485d1741adSGustavo Padovan 		if (exynos_crtc->type == out_type)
2498a326eddSGustavo Padovan 			return exynos_crtc->pipe;
250f37cd5e8SInki Dae 	}
251f37cd5e8SInki Dae 
252f37cd5e8SInki Dae 	return -EPERM;
253f37cd5e8SInki Dae }
2545595d4d8SYoungJun Cho 
2555595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
2565595d4d8SYoungJun Cho {
25793bca243SGustavo Padovan 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
2585595d4d8SYoungJun Cho 
25993bca243SGustavo Padovan 	if (exynos_crtc->ops->te_handler)
26093bca243SGustavo Padovan 		exynos_crtc->ops->te_handler(exynos_crtc);
2615595d4d8SYoungJun Cho }
262