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  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  */
28 
29 #include "drmP.h"
30 #include "drm_crtc_helper.h"
31 
32 #include "exynos_drm_drv.h"
33 #include "exynos_drm_encoder.h"
34 #include "exynos_drm_plane.h"
35 
36 #define to_exynos_crtc(x)	container_of(x, struct exynos_drm_crtc,\
37 				drm_crtc)
38 
39 enum exynos_crtc_mode {
40 	CRTC_MODE_NORMAL,	/* normal mode */
41 	CRTC_MODE_BLANK,	/* The private plane of crtc is blank */
42 };
43 
44 /*
45  * Exynos specific crtc structure.
46  *
47  * @drm_crtc: crtc object.
48  * @drm_plane: pointer of private plane object for this crtc
49  * @pipe: a crtc index created at load() with a new crtc object creation
50  *	and the crtc object would be set to private->crtc array
51  *	to get a crtc object corresponding to this pipe from private->crtc
52  *	array when irq interrupt occured. the reason of using this pipe is that
53  *	drm framework doesn't support multiple irq yet.
54  *	we can refer to the crtc to current hardware interrupt occured through
55  *	this pipe value.
56  * @dpms: store the crtc dpms value
57  * @mode: store the crtc mode value
58  */
59 struct exynos_drm_crtc {
60 	struct drm_crtc			drm_crtc;
61 	struct drm_plane		*plane;
62 	unsigned int			pipe;
63 	unsigned int			dpms;
64 	enum exynos_crtc_mode		mode;
65 };
66 
67 static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
68 {
69 	struct drm_device *dev = crtc->dev;
70 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
71 
72 	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
73 
74 	if (exynos_crtc->dpms == mode) {
75 		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
76 		return;
77 	}
78 
79 	mutex_lock(&dev->struct_mutex);
80 
81 	exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
82 	exynos_crtc->dpms = mode;
83 
84 	mutex_unlock(&dev->struct_mutex);
85 }
86 
87 static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
88 {
89 	DRM_DEBUG_KMS("%s\n", __FILE__);
90 
91 	/* drm framework doesn't check NULL. */
92 }
93 
94 static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
95 {
96 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
97 
98 	DRM_DEBUG_KMS("%s\n", __FILE__);
99 
100 	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
101 	exynos_plane_commit(exynos_crtc->plane);
102 	exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
103 }
104 
105 static bool
106 exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
107 			    const struct drm_display_mode *mode,
108 			    struct drm_display_mode *adjusted_mode)
109 {
110 	DRM_DEBUG_KMS("%s\n", __FILE__);
111 
112 	/* drm framework doesn't check NULL */
113 	return true;
114 }
115 
116 static int
117 exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
118 			  struct drm_display_mode *adjusted_mode, int x, int y,
119 			  struct drm_framebuffer *old_fb)
120 {
121 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
122 	struct drm_plane *plane = exynos_crtc->plane;
123 	unsigned int crtc_w;
124 	unsigned int crtc_h;
125 	int pipe = exynos_crtc->pipe;
126 	int ret;
127 
128 	DRM_DEBUG_KMS("%s\n", __FILE__);
129 
130 	/*
131 	 * copy the mode data adjusted by mode_fixup() into crtc->mode
132 	 * so that hardware can be seet to proper mode.
133 	 */
134 	memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
135 
136 	crtc_w = crtc->fb->width - x;
137 	crtc_h = crtc->fb->height - y;
138 
139 	ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
140 				    x, y, crtc_w, crtc_h);
141 	if (ret)
142 		return ret;
143 
144 	plane->crtc = crtc;
145 	plane->fb = crtc->fb;
146 
147 	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
148 
149 	return 0;
150 }
151 
152 static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
153 					  struct drm_framebuffer *old_fb)
154 {
155 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
156 	struct drm_plane *plane = exynos_crtc->plane;
157 	unsigned int crtc_w;
158 	unsigned int crtc_h;
159 	int ret;
160 
161 	DRM_DEBUG_KMS("%s\n", __FILE__);
162 
163 	crtc_w = crtc->fb->width - x;
164 	crtc_h = crtc->fb->height - y;
165 
166 	ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
167 				    x, y, crtc_w, crtc_h);
168 	if (ret)
169 		return ret;
170 
171 	exynos_drm_crtc_commit(crtc);
172 
173 	return 0;
174 }
175 
176 static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
177 {
178 	DRM_DEBUG_KMS("%s\n", __FILE__);
179 	/* drm framework doesn't check NULL */
180 }
181 
182 static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
183 {
184 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
185 
186 	DRM_DEBUG_KMS("%s\n", __FILE__);
187 
188 	exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
189 	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
190 }
191 
192 static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
193 	.dpms		= exynos_drm_crtc_dpms,
194 	.prepare	= exynos_drm_crtc_prepare,
195 	.commit		= exynos_drm_crtc_commit,
196 	.mode_fixup	= exynos_drm_crtc_mode_fixup,
197 	.mode_set	= exynos_drm_crtc_mode_set,
198 	.mode_set_base	= exynos_drm_crtc_mode_set_base,
199 	.load_lut	= exynos_drm_crtc_load_lut,
200 	.disable	= exynos_drm_crtc_disable,
201 };
202 
203 static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
204 				      struct drm_framebuffer *fb,
205 				      struct drm_pending_vblank_event *event)
206 {
207 	struct drm_device *dev = crtc->dev;
208 	struct exynos_drm_private *dev_priv = dev->dev_private;
209 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
210 	struct drm_framebuffer *old_fb = crtc->fb;
211 	int ret = -EINVAL;
212 
213 	DRM_DEBUG_KMS("%s\n", __FILE__);
214 
215 	mutex_lock(&dev->struct_mutex);
216 
217 	if (event) {
218 		/*
219 		 * the pipe from user always is 0 so we can set pipe number
220 		 * of current owner to event.
221 		 */
222 		event->pipe = exynos_crtc->pipe;
223 
224 		ret = drm_vblank_get(dev, exynos_crtc->pipe);
225 		if (ret) {
226 			DRM_DEBUG("failed to acquire vblank counter\n");
227 			list_del(&event->base.link);
228 
229 			goto out;
230 		}
231 
232 		list_add_tail(&event->base.link,
233 				&dev_priv->pageflip_event_list);
234 
235 		crtc->fb = fb;
236 		ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
237 						    NULL);
238 		if (ret) {
239 			crtc->fb = old_fb;
240 			drm_vblank_put(dev, exynos_crtc->pipe);
241 			list_del(&event->base.link);
242 
243 			goto out;
244 		}
245 	}
246 out:
247 	mutex_unlock(&dev->struct_mutex);
248 	return ret;
249 }
250 
251 static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
252 {
253 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
254 	struct exynos_drm_private *private = crtc->dev->dev_private;
255 
256 	DRM_DEBUG_KMS("%s\n", __FILE__);
257 
258 	private->crtc[exynos_crtc->pipe] = NULL;
259 
260 	drm_crtc_cleanup(crtc);
261 	kfree(exynos_crtc);
262 }
263 
264 static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
265 					struct drm_property *property,
266 					uint64_t val)
267 {
268 	struct drm_device *dev = crtc->dev;
269 	struct exynos_drm_private *dev_priv = dev->dev_private;
270 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
271 
272 	DRM_DEBUG_KMS("%s\n", __func__);
273 
274 	if (property == dev_priv->crtc_mode_property) {
275 		enum exynos_crtc_mode mode = val;
276 
277 		if (mode == exynos_crtc->mode)
278 			return 0;
279 
280 		exynos_crtc->mode = mode;
281 
282 		switch (mode) {
283 		case CRTC_MODE_NORMAL:
284 			exynos_drm_crtc_commit(crtc);
285 			break;
286 		case CRTC_MODE_BLANK:
287 			exynos_plane_dpms(exynos_crtc->plane,
288 					  DRM_MODE_DPMS_OFF);
289 			break;
290 		default:
291 			break;
292 		}
293 
294 		return 0;
295 	}
296 
297 	return -EINVAL;
298 }
299 
300 static struct drm_crtc_funcs exynos_crtc_funcs = {
301 	.set_config	= drm_crtc_helper_set_config,
302 	.page_flip	= exynos_drm_crtc_page_flip,
303 	.destroy	= exynos_drm_crtc_destroy,
304 	.set_property	= exynos_drm_crtc_set_property,
305 };
306 
307 static const struct drm_prop_enum_list mode_names[] = {
308 	{ CRTC_MODE_NORMAL, "normal" },
309 	{ CRTC_MODE_BLANK, "blank" },
310 };
311 
312 static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
313 {
314 	struct drm_device *dev = crtc->dev;
315 	struct exynos_drm_private *dev_priv = dev->dev_private;
316 	struct drm_property *prop;
317 
318 	DRM_DEBUG_KMS("%s\n", __func__);
319 
320 	prop = dev_priv->crtc_mode_property;
321 	if (!prop) {
322 		prop = drm_property_create_enum(dev, 0, "mode", mode_names,
323 						ARRAY_SIZE(mode_names));
324 		if (!prop)
325 			return;
326 
327 		dev_priv->crtc_mode_property = prop;
328 	}
329 
330 	drm_object_attach_property(&crtc->base, prop, 0);
331 }
332 
333 int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
334 {
335 	struct exynos_drm_crtc *exynos_crtc;
336 	struct exynos_drm_private *private = dev->dev_private;
337 	struct drm_crtc *crtc;
338 
339 	DRM_DEBUG_KMS("%s\n", __FILE__);
340 
341 	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
342 	if (!exynos_crtc) {
343 		DRM_ERROR("failed to allocate exynos crtc\n");
344 		return -ENOMEM;
345 	}
346 
347 	exynos_crtc->pipe = nr;
348 	exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
349 	exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
350 	if (!exynos_crtc->plane) {
351 		kfree(exynos_crtc);
352 		return -ENOMEM;
353 	}
354 
355 	crtc = &exynos_crtc->drm_crtc;
356 
357 	private->crtc[nr] = crtc;
358 
359 	drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
360 	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
361 
362 	exynos_drm_crtc_attach_mode_property(crtc);
363 
364 	return 0;
365 }
366 
367 int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
368 {
369 	struct exynos_drm_private *private = dev->dev_private;
370 	struct exynos_drm_crtc *exynos_crtc =
371 		to_exynos_crtc(private->crtc[crtc]);
372 
373 	DRM_DEBUG_KMS("%s\n", __FILE__);
374 
375 	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
376 		return -EPERM;
377 
378 	exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
379 			exynos_drm_enable_vblank);
380 
381 	return 0;
382 }
383 
384 void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
385 {
386 	struct exynos_drm_private *private = dev->dev_private;
387 	struct exynos_drm_crtc *exynos_crtc =
388 		to_exynos_crtc(private->crtc[crtc]);
389 
390 	DRM_DEBUG_KMS("%s\n", __FILE__);
391 
392 	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
393 		return;
394 
395 	exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
396 			exynos_drm_disable_vblank);
397 }
398