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
18#include "exynos_drm_crtc.h"
19#include "exynos_drm_drv.h"
20#include "exynos_drm_encoder.h"
21#include "exynos_drm_plane.h"
22
23static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
24{
25	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
26
27	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
28
29	if (exynos_crtc->dpms == mode) {
30		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
31		return;
32	}
33
34	if (mode > DRM_MODE_DPMS_ON) {
35		/* wait for the completion of page flip. */
36		if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
37				(exynos_crtc->event == NULL), HZ/20))
38			exynos_crtc->event = NULL;
39		drm_crtc_vblank_off(crtc);
40	}
41
42	if (exynos_crtc->ops->dpms)
43		exynos_crtc->ops->dpms(exynos_crtc, mode);
44
45	exynos_crtc->dpms = mode;
46
47	if (mode == DRM_MODE_DPMS_ON)
48		drm_crtc_vblank_on(crtc);
49}
50
51static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
52{
53	/* drm framework doesn't check NULL. */
54}
55
56static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
57{
58	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
59	struct exynos_drm_plane *exynos_plane = to_exynos_plane(crtc->primary);
60
61	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
62
63	if (exynos_crtc->ops->win_commit)
64		exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
65
66	if (exynos_crtc->ops->commit)
67		exynos_crtc->ops->commit(exynos_crtc);
68}
69
70static bool
71exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
72			    const struct drm_display_mode *mode,
73			    struct drm_display_mode *adjusted_mode)
74{
75	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
76
77	if (exynos_crtc->ops->mode_fixup)
78		return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
79						    adjusted_mode);
80
81	return true;
82}
83
84static int
85exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
86			  struct drm_display_mode *adjusted_mode, int x, int y,
87			  struct drm_framebuffer *old_fb)
88{
89	struct drm_framebuffer *fb = crtc->primary->fb;
90	unsigned int crtc_w;
91	unsigned int crtc_h;
92	int ret;
93
94	/*
95	 * copy the mode data adjusted by mode_fixup() into crtc->mode
96	 * so that hardware can be seet to proper mode.
97	 */
98	memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
99
100	ret = exynos_check_plane(crtc->primary, fb);
101	if (ret < 0)
102		return ret;
103
104	crtc_w = fb->width - x;
105	crtc_h = fb->height - y;
106	exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
107			      crtc_w, crtc_h, x, y, crtc_w, crtc_h);
108
109	return 0;
110}
111
112static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
113					  struct drm_framebuffer *old_fb)
114{
115	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
116	struct drm_framebuffer *fb = crtc->primary->fb;
117	unsigned int crtc_w;
118	unsigned int crtc_h;
119
120	/* when framebuffer changing is requested, crtc's dpms should be on */
121	if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
122		DRM_ERROR("failed framebuffer changing request.\n");
123		return -EPERM;
124	}
125
126	crtc_w = fb->width - x;
127	crtc_h = fb->height - y;
128
129	return exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
130				   crtc_w, crtc_h, x, y, crtc_w, crtc_h);
131}
132
133static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
134{
135	struct drm_plane *plane;
136	int ret;
137
138	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
139
140	drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
141		if (plane->crtc != crtc)
142			continue;
143
144		ret = plane->funcs->disable_plane(plane);
145		if (ret)
146			DRM_ERROR("Failed to disable plane %d\n", ret);
147	}
148}
149
150static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
151	.dpms		= exynos_drm_crtc_dpms,
152	.prepare	= exynos_drm_crtc_prepare,
153	.commit		= exynos_drm_crtc_commit,
154	.mode_fixup	= exynos_drm_crtc_mode_fixup,
155	.mode_set	= exynos_drm_crtc_mode_set,
156	.mode_set_base	= exynos_drm_crtc_mode_set_base,
157	.disable	= exynos_drm_crtc_disable,
158};
159
160static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
161				     struct drm_framebuffer *fb,
162				     struct drm_pending_vblank_event *event,
163				     uint32_t page_flip_flags)
164{
165	struct drm_device *dev = crtc->dev;
166	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
167	struct drm_framebuffer *old_fb = crtc->primary->fb;
168	unsigned int crtc_w, crtc_h;
169	int ret;
170
171	/* when the page flip is requested, crtc's dpms should be on */
172	if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
173		DRM_ERROR("failed page flip request.\n");
174		return -EINVAL;
175	}
176
177	if (!event)
178		return -EINVAL;
179
180	spin_lock_irq(&dev->event_lock);
181	if (exynos_crtc->event) {
182		ret = -EBUSY;
183		goto out;
184	}
185
186	ret = drm_vblank_get(dev, exynos_crtc->pipe);
187	if (ret) {
188		DRM_DEBUG("failed to acquire vblank counter\n");
189		goto out;
190	}
191
192	exynos_crtc->event = event;
193	spin_unlock_irq(&dev->event_lock);
194
195	/*
196	 * the pipe from user always is 0 so we can set pipe number
197	 * of current owner to event.
198	 */
199	event->pipe = exynos_crtc->pipe;
200
201	crtc->primary->fb = fb;
202	crtc_w = fb->width - crtc->x;
203	crtc_h = fb->height - crtc->y;
204	ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
205				  crtc_w, crtc_h, crtc->x, crtc->y,
206				  crtc_w, crtc_h);
207	if (ret) {
208		crtc->primary->fb = old_fb;
209		spin_lock_irq(&dev->event_lock);
210		exynos_crtc->event = NULL;
211		drm_vblank_put(dev, exynos_crtc->pipe);
212		spin_unlock_irq(&dev->event_lock);
213		return ret;
214	}
215
216	return 0;
217
218out:
219	spin_unlock_irq(&dev->event_lock);
220	return ret;
221}
222
223static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
224{
225	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
226	struct exynos_drm_private *private = crtc->dev->dev_private;
227
228	private->crtc[exynos_crtc->pipe] = NULL;
229
230	drm_crtc_cleanup(crtc);
231	kfree(exynos_crtc);
232}
233
234static struct drm_crtc_funcs exynos_crtc_funcs = {
235	.set_config	= drm_crtc_helper_set_config,
236	.page_flip	= exynos_drm_crtc_page_flip,
237	.destroy	= exynos_drm_crtc_destroy,
238};
239
240struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
241					struct drm_plane *plane,
242					int pipe,
243					enum exynos_drm_output_type type,
244					const struct exynos_drm_crtc_ops *ops,
245					void *ctx)
246{
247	struct exynos_drm_crtc *exynos_crtc;
248	struct exynos_drm_private *private = drm_dev->dev_private;
249	struct drm_crtc *crtc;
250	int ret;
251
252	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
253	if (!exynos_crtc)
254		return ERR_PTR(-ENOMEM);
255
256	init_waitqueue_head(&exynos_crtc->pending_flip_queue);
257
258	exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
259	exynos_crtc->pipe = pipe;
260	exynos_crtc->type = type;
261	exynos_crtc->ops = ops;
262	exynos_crtc->ctx = ctx;
263
264	crtc = &exynos_crtc->base;
265
266	private->crtc[pipe] = crtc;
267
268	ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
269					&exynos_crtc_funcs);
270	if (ret < 0)
271		goto err_crtc;
272
273	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
274
275	return exynos_crtc;
276
277err_crtc:
278	plane->funcs->destroy(plane);
279	kfree(exynos_crtc);
280	return ERR_PTR(ret);
281}
282
283int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
284{
285	struct exynos_drm_private *private = dev->dev_private;
286	struct exynos_drm_crtc *exynos_crtc =
287		to_exynos_crtc(private->crtc[pipe]);
288
289	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
290		return -EPERM;
291
292	if (exynos_crtc->ops->enable_vblank)
293		exynos_crtc->ops->enable_vblank(exynos_crtc);
294
295	return 0;
296}
297
298void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
299{
300	struct exynos_drm_private *private = dev->dev_private;
301	struct exynos_drm_crtc *exynos_crtc =
302		to_exynos_crtc(private->crtc[pipe]);
303
304	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
305		return;
306
307	if (exynos_crtc->ops->disable_vblank)
308		exynos_crtc->ops->disable_vblank(exynos_crtc);
309}
310
311void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
312{
313	struct exynos_drm_private *dev_priv = dev->dev_private;
314	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
315	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
316	unsigned long flags;
317
318	spin_lock_irqsave(&dev->event_lock, flags);
319	if (exynos_crtc->event) {
320
321		drm_send_vblank_event(dev, -1, exynos_crtc->event);
322		drm_vblank_put(dev, pipe);
323		wake_up(&exynos_crtc->pending_flip_queue);
324
325	}
326
327	exynos_crtc->event = NULL;
328	spin_unlock_irqrestore(&dev->event_lock, flags);
329}
330
331void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
332{
333	struct exynos_drm_crtc *exynos_crtc;
334	struct drm_device *dev = fb->dev;
335	struct drm_crtc *crtc;
336
337	/*
338	 * make sure that overlay data are updated to real hardware
339	 * for all encoders.
340	 */
341	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
342		exynos_crtc = to_exynos_crtc(crtc);
343
344		/*
345		 * wait for vblank interrupt
346		 * - this makes sure that overlay data are updated to
347		 *	real hardware.
348		 */
349		if (exynos_crtc->ops->wait_for_vblank)
350			exynos_crtc->ops->wait_for_vblank(exynos_crtc);
351	}
352}
353
354int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
355					unsigned int out_type)
356{
357	struct drm_crtc *crtc;
358
359	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
360		struct exynos_drm_crtc *exynos_crtc;
361
362		exynos_crtc = to_exynos_crtc(crtc);
363		if (exynos_crtc->type == out_type)
364			return exynos_crtc->pipe;
365	}
366
367	return -EPERM;
368}
369
370void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
371{
372	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
373
374	if (exynos_crtc->ops->te_handler)
375		exynos_crtc->ops->te_handler(exynos_crtc);
376}
377