1/* exynos_drm_crtc.c
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Authors:
5 *	Inki Dae <inki.dae@samsung.com>
6 *	Joonyoung Shim <jy0922.shim@samsung.com>
7 *	Seung-Woo Kim <sw0312.kim@samsung.com>
8 *
9 * This program is free software; you can redistribute  it and/or modify it
10 * under  the terms of  the GNU General  Public License as published by the
11 * Free Software Foundation;  either version 2 of the  License, or (at your
12 * option) any later version.
13 */
14
15#include <drm/drmP.h>
16#include <drm/drm_crtc_helper.h>
17#include <drm/drm_atomic.h>
18#include <drm/drm_atomic_helper.h>
19
20#include "exynos_drm_crtc.h"
21#include "exynos_drm_drv.h"
22#include "exynos_drm_plane.h"
23
24static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
25{
26	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
27
28	if (exynos_crtc->ops->enable)
29		exynos_crtc->ops->enable(exynos_crtc);
30
31	drm_crtc_vblank_on(crtc);
32}
33
34static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
35{
36	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
37
38	drm_crtc_vblank_off(crtc);
39
40	if (exynos_crtc->ops->disable)
41		exynos_crtc->ops->disable(exynos_crtc);
42}
43
44static void
45exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
46{
47	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
48
49	if (exynos_crtc->ops->commit)
50		exynos_crtc->ops->commit(exynos_crtc);
51}
52
53static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
54				     struct drm_crtc_state *state)
55{
56	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
57
58	if (!state->enable)
59		return 0;
60
61	if (exynos_crtc->ops->atomic_check)
62		return exynos_crtc->ops->atomic_check(exynos_crtc, state);
63
64	return 0;
65}
66
67static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
68				     struct drm_crtc_state *old_crtc_state)
69{
70	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
71	struct drm_plane *plane;
72
73	exynos_crtc->event = crtc->state->event;
74
75	drm_atomic_crtc_for_each_plane(plane, crtc) {
76		struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
77
78		if (exynos_crtc->ops->atomic_begin)
79			exynos_crtc->ops->atomic_begin(exynos_crtc,
80							exynos_plane);
81	}
82}
83
84static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
85				     struct drm_crtc_state *old_crtc_state)
86{
87	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
88	struct drm_plane *plane;
89
90	drm_atomic_crtc_for_each_plane(plane, crtc) {
91		struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
92
93		if (exynos_crtc->ops->atomic_flush)
94			exynos_crtc->ops->atomic_flush(exynos_crtc,
95							exynos_plane);
96	}
97}
98
99static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
100	.enable		= exynos_drm_crtc_enable,
101	.disable	= exynos_drm_crtc_disable,
102	.mode_set_nofb	= exynos_drm_crtc_mode_set_nofb,
103	.atomic_check	= exynos_crtc_atomic_check,
104	.atomic_begin	= exynos_crtc_atomic_begin,
105	.atomic_flush	= exynos_crtc_atomic_flush,
106};
107
108static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
109{
110	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
111	struct exynos_drm_private *private = crtc->dev->dev_private;
112
113	private->crtc[exynos_crtc->pipe] = NULL;
114
115	drm_crtc_cleanup(crtc);
116	kfree(exynos_crtc);
117}
118
119static struct drm_crtc_funcs exynos_crtc_funcs = {
120	.set_config	= drm_atomic_helper_set_config,
121	.page_flip	= drm_atomic_helper_page_flip,
122	.destroy	= exynos_drm_crtc_destroy,
123	.reset = drm_atomic_helper_crtc_reset,
124	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
125	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
126};
127
128struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
129					struct drm_plane *plane,
130					int pipe,
131					enum exynos_drm_output_type type,
132					const struct exynos_drm_crtc_ops *ops,
133					void *ctx)
134{
135	struct exynos_drm_crtc *exynos_crtc;
136	struct exynos_drm_private *private = drm_dev->dev_private;
137	struct drm_crtc *crtc;
138	int ret;
139
140	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
141	if (!exynos_crtc)
142		return ERR_PTR(-ENOMEM);
143
144	exynos_crtc->pipe = pipe;
145	exynos_crtc->type = type;
146	exynos_crtc->ops = ops;
147	exynos_crtc->ctx = ctx;
148
149	init_waitqueue_head(&exynos_crtc->wait_update);
150
151	crtc = &exynos_crtc->base;
152
153	private->crtc[pipe] = crtc;
154
155	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
156					&exynos_crtc_funcs);
157	if (ret < 0)
158		goto err_crtc;
159
160	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
161
162	return exynos_crtc;
163
164err_crtc:
165	plane->funcs->destroy(plane);
166	kfree(exynos_crtc);
167	return ERR_PTR(ret);
168}
169
170int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
171{
172	struct exynos_drm_private *private = dev->dev_private;
173	struct exynos_drm_crtc *exynos_crtc =
174		to_exynos_crtc(private->crtc[pipe]);
175
176	if (exynos_crtc->ops->enable_vblank)
177		return exynos_crtc->ops->enable_vblank(exynos_crtc);
178
179	return 0;
180}
181
182void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
183{
184	struct exynos_drm_private *private = dev->dev_private;
185	struct exynos_drm_crtc *exynos_crtc =
186		to_exynos_crtc(private->crtc[pipe]);
187
188	if (exynos_crtc->ops->disable_vblank)
189		exynos_crtc->ops->disable_vblank(exynos_crtc);
190}
191
192void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
193{
194	wait_event_timeout(exynos_crtc->wait_update,
195			   (atomic_read(&exynos_crtc->pending_update) == 0),
196			   msecs_to_jiffies(50));
197}
198
199void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
200				struct exynos_drm_plane *exynos_plane)
201{
202	struct drm_crtc *crtc = &exynos_crtc->base;
203	unsigned long flags;
204
205	exynos_plane->pending_fb = NULL;
206
207	if (atomic_dec_and_test(&exynos_crtc->pending_update))
208		wake_up(&exynos_crtc->wait_update);
209
210	spin_lock_irqsave(&crtc->dev->event_lock, flags);
211	if (exynos_crtc->event)
212		drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
213
214	exynos_crtc->event = NULL;
215	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
216}
217
218void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
219{
220	struct exynos_drm_crtc *exynos_crtc;
221	struct drm_device *dev = fb->dev;
222	struct drm_crtc *crtc;
223
224	/*
225	 * make sure that overlay data are updated to real hardware
226	 * for all encoders.
227	 */
228	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
229		exynos_crtc = to_exynos_crtc(crtc);
230
231		/*
232		 * wait for vblank interrupt
233		 * - this makes sure that overlay data are updated to
234		 *	real hardware.
235		 */
236		if (exynos_crtc->ops->wait_for_vblank)
237			exynos_crtc->ops->wait_for_vblank(exynos_crtc);
238	}
239}
240
241int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
242				       enum exynos_drm_output_type out_type)
243{
244	struct drm_crtc *crtc;
245
246	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
247		struct exynos_drm_crtc *exynos_crtc;
248
249		exynos_crtc = to_exynos_crtc(crtc);
250		if (exynos_crtc->type == out_type)
251			return exynos_crtc->pipe;
252	}
253
254	return -EPERM;
255}
256
257void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
258{
259	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
260
261	if (exynos_crtc->ops->te_handler)
262		exynos_crtc->ops->te_handler(exynos_crtc);
263}
264