Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 23#define to_exynos_crtc(x)	container_of(x, struct exynos_drm_crtc,\
 24				drm_crtc)
 25
 26enum exynos_crtc_mode {
 27	CRTC_MODE_NORMAL,	/* normal mode */
 28	CRTC_MODE_BLANK,	/* The private plane of crtc is blank */
 29};
 30
 31/*
 32 * Exynos specific crtc structure.
 33 *
 34 * @drm_crtc: crtc object.
 35 * @drm_plane: pointer of private plane object for this crtc
 36 * @manager: the manager associated with this crtc
 37 * @pipe: a crtc index created at load() with a new crtc object creation
 38 *	and the crtc object would be set to private->crtc array
 39 *	to get a crtc object corresponding to this pipe from private->crtc
 40 *	array when irq interrupt occurred. the reason of using this pipe is that
 41 *	drm framework doesn't support multiple irq yet.
 42 *	we can refer to the crtc to current hardware interrupt occurred through
 43 *	this pipe value.
 44 * @dpms: store the crtc dpms value
 45 * @mode: store the crtc mode value
 46 */
 47struct exynos_drm_crtc {
 48	struct drm_crtc			drm_crtc;
 49	struct drm_plane		*plane;
 50	struct exynos_drm_manager	*manager;
 51	unsigned int			pipe;
 52	unsigned int			dpms;
 53	enum exynos_crtc_mode		mode;
 54	wait_queue_head_t		pending_flip_queue;
 55	atomic_t			pending_flip;
 56};
 57
 58static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
 59{
 60	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 61	struct exynos_drm_manager *manager = exynos_crtc->manager;
 62
 63	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
 64
 65	if (exynos_crtc->dpms == mode) {
 66		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
 67		return;
 68	}
 69
 70	if (mode > DRM_MODE_DPMS_ON) {
 71		/* wait for the completion of page flip. */
 72		wait_event(exynos_crtc->pending_flip_queue,
 73				atomic_read(&exynos_crtc->pending_flip) == 0);
 74		drm_vblank_off(crtc->dev, exynos_crtc->pipe);
 75	}
 76
 77	if (manager->ops->dpms)
 78		manager->ops->dpms(manager, mode);
 79
 80	exynos_crtc->dpms = mode;
 81}
 82
 83static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
 84{
 85	/* drm framework doesn't check NULL. */
 86}
 87
 88static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
 89{
 90	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 91	struct exynos_drm_manager *manager = exynos_crtc->manager;
 92
 93	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
 94
 95	exynos_plane_commit(exynos_crtc->plane);
 96
 97	if (manager->ops->commit)
 98		manager->ops->commit(manager);
 99
100	exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
101}
102
103static bool
104exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
105			    const struct drm_display_mode *mode,
106			    struct drm_display_mode *adjusted_mode)
107{
108	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
109	struct exynos_drm_manager *manager = exynos_crtc->manager;
110
111	if (manager->ops->mode_fixup)
112		return manager->ops->mode_fixup(manager, mode, adjusted_mode);
113
114	return true;
115}
116
117static int
118exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
119			  struct drm_display_mode *adjusted_mode, int x, int y,
120			  struct drm_framebuffer *old_fb)
121{
122	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
123	struct exynos_drm_manager *manager = exynos_crtc->manager;
124	struct drm_plane *plane = exynos_crtc->plane;
125	unsigned int crtc_w;
126	unsigned int crtc_h;
127	int ret;
128
129	/*
130	 * copy the mode data adjusted by mode_fixup() into crtc->mode
131	 * so that hardware can be seet to proper mode.
132	 */
133	memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
134
135	crtc_w = crtc->primary->fb->width - x;
136	crtc_h = crtc->primary->fb->height - y;
137
138	if (manager->ops->mode_set)
139		manager->ops->mode_set(manager, &crtc->mode);
140
141	ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
142				    x, y, crtc_w, crtc_h);
143	if (ret)
144		return ret;
145
146	plane->crtc = crtc;
147	plane->fb = crtc->primary->fb;
148	drm_framebuffer_reference(plane->fb);
149
150	return 0;
151}
152
153static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
154					  struct drm_framebuffer *old_fb)
155{
156	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
157	struct drm_plane *plane = exynos_crtc->plane;
158	unsigned int crtc_w;
159	unsigned int crtc_h;
160	int ret;
161
162	/* when framebuffer changing is requested, crtc's dpms should be on */
163	if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
164		DRM_ERROR("failed framebuffer changing request.\n");
165		return -EPERM;
166	}
167
168	crtc_w = crtc->primary->fb->width - x;
169	crtc_h = crtc->primary->fb->height - y;
170
171	ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
172				    x, y, crtc_w, crtc_h);
173	if (ret)
174		return ret;
175
176	exynos_drm_crtc_commit(crtc);
177
178	return 0;
179}
180
181static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
182					  struct drm_framebuffer *old_fb)
183{
184	return exynos_drm_crtc_mode_set_commit(crtc, x, y, old_fb);
185}
186
187static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
188{
189	struct drm_plane *plane;
190	int ret;
191
192	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
193
194	drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
195		if (plane->crtc != crtc)
196			continue;
197
198		ret = plane->funcs->disable_plane(plane);
199		if (ret)
200			DRM_ERROR("Failed to disable plane %d\n", ret);
201	}
202}
203
204static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
205	.dpms		= exynos_drm_crtc_dpms,
206	.prepare	= exynos_drm_crtc_prepare,
207	.commit		= exynos_drm_crtc_commit,
208	.mode_fixup	= exynos_drm_crtc_mode_fixup,
209	.mode_set	= exynos_drm_crtc_mode_set,
210	.mode_set_base	= exynos_drm_crtc_mode_set_base,
211	.disable	= exynos_drm_crtc_disable,
212};
213
214static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
215				     struct drm_framebuffer *fb,
216				     struct drm_pending_vblank_event *event,
217				     uint32_t page_flip_flags)
218{
219	struct drm_device *dev = crtc->dev;
220	struct exynos_drm_private *dev_priv = dev->dev_private;
221	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
222	struct drm_framebuffer *old_fb = crtc->primary->fb;
223	int ret = -EINVAL;
224
225	/* when the page flip is requested, crtc's dpms should be on */
226	if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
227		DRM_ERROR("failed page flip request.\n");
228		return -EINVAL;
229	}
230
231	mutex_lock(&dev->struct_mutex);
232
233	if (event) {
234		/*
235		 * the pipe from user always is 0 so we can set pipe number
236		 * of current owner to event.
237		 */
238		event->pipe = exynos_crtc->pipe;
239
240		ret = drm_vblank_get(dev, exynos_crtc->pipe);
241		if (ret) {
242			DRM_DEBUG("failed to acquire vblank counter\n");
243
244			goto out;
245		}
246
247		spin_lock_irq(&dev->event_lock);
248		list_add_tail(&event->base.link,
249				&dev_priv->pageflip_event_list);
250		atomic_set(&exynos_crtc->pending_flip, 1);
251		spin_unlock_irq(&dev->event_lock);
252
253		crtc->primary->fb = fb;
254		ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,
255						    NULL);
256		if (ret) {
257			crtc->primary->fb = old_fb;
258
259			spin_lock_irq(&dev->event_lock);
260			drm_vblank_put(dev, exynos_crtc->pipe);
261			list_del(&event->base.link);
262			spin_unlock_irq(&dev->event_lock);
263
264			goto out;
265		}
266	}
267out:
268	mutex_unlock(&dev->struct_mutex);
269	return ret;
270}
271
272static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
273{
274	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
275	struct exynos_drm_private *private = crtc->dev->dev_private;
276
277	private->crtc[exynos_crtc->pipe] = NULL;
278
279	drm_crtc_cleanup(crtc);
280	kfree(exynos_crtc);
281}
282
283static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
284					struct drm_property *property,
285					uint64_t val)
286{
287	struct drm_device *dev = crtc->dev;
288	struct exynos_drm_private *dev_priv = dev->dev_private;
289	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
290
291	if (property == dev_priv->crtc_mode_property) {
292		enum exynos_crtc_mode mode = val;
293
294		if (mode == exynos_crtc->mode)
295			return 0;
296
297		exynos_crtc->mode = mode;
298
299		switch (mode) {
300		case CRTC_MODE_NORMAL:
301			exynos_drm_crtc_commit(crtc);
302			break;
303		case CRTC_MODE_BLANK:
304			exynos_plane_dpms(exynos_crtc->plane,
305					  DRM_MODE_DPMS_OFF);
306			break;
307		default:
308			break;
309		}
310
311		return 0;
312	}
313
314	return -EINVAL;
315}
316
317static struct drm_crtc_funcs exynos_crtc_funcs = {
318	.set_config	= drm_crtc_helper_set_config,
319	.page_flip	= exynos_drm_crtc_page_flip,
320	.destroy	= exynos_drm_crtc_destroy,
321	.set_property	= exynos_drm_crtc_set_property,
322};
323
324static const struct drm_prop_enum_list mode_names[] = {
325	{ CRTC_MODE_NORMAL, "normal" },
326	{ CRTC_MODE_BLANK, "blank" },
327};
328
329static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
330{
331	struct drm_device *dev = crtc->dev;
332	struct exynos_drm_private *dev_priv = dev->dev_private;
333	struct drm_property *prop;
334
335	prop = dev_priv->crtc_mode_property;
336	if (!prop) {
337		prop = drm_property_create_enum(dev, 0, "mode", mode_names,
338						ARRAY_SIZE(mode_names));
339		if (!prop)
340			return;
341
342		dev_priv->crtc_mode_property = prop;
343	}
344
345	drm_object_attach_property(&crtc->base, prop, 0);
346}
347
348int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
349{
350	struct exynos_drm_crtc *exynos_crtc;
351	struct exynos_drm_private *private = manager->drm_dev->dev_private;
352	struct drm_crtc *crtc;
353
354	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
355	if (!exynos_crtc)
356		return -ENOMEM;
357
358	init_waitqueue_head(&exynos_crtc->pending_flip_queue);
359	atomic_set(&exynos_crtc->pending_flip, 0);
360
361	exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
362	exynos_crtc->manager = manager;
363	exynos_crtc->pipe = manager->pipe;
364	exynos_crtc->plane = exynos_plane_init(manager->drm_dev,
365				1 << manager->pipe, true);
366	if (!exynos_crtc->plane) {
367		kfree(exynos_crtc);
368		return -ENOMEM;
369	}
370
371	crtc = &exynos_crtc->drm_crtc;
372
373	private->crtc[manager->pipe] = crtc;
374
375	drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);
376	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
377
378	exynos_drm_crtc_attach_mode_property(crtc);
379
380	return 0;
381}
382
383int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
384{
385	struct exynos_drm_private *private = dev->dev_private;
386	struct exynos_drm_crtc *exynos_crtc =
387		to_exynos_crtc(private->crtc[pipe]);
388	struct exynos_drm_manager *manager = exynos_crtc->manager;
389
390	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
391		return -EPERM;
392
393	if (manager->ops->enable_vblank)
394		manager->ops->enable_vblank(manager);
395
396	return 0;
397}
398
399void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
400{
401	struct exynos_drm_private *private = dev->dev_private;
402	struct exynos_drm_crtc *exynos_crtc =
403		to_exynos_crtc(private->crtc[pipe]);
404	struct exynos_drm_manager *manager = exynos_crtc->manager;
405
406	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
407		return;
408
409	if (manager->ops->disable_vblank)
410		manager->ops->disable_vblank(manager);
411}
412
413void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
414{
415	struct exynos_drm_private *dev_priv = dev->dev_private;
416	struct drm_pending_vblank_event *e, *t;
417	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
418	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
419	unsigned long flags;
420
421	spin_lock_irqsave(&dev->event_lock, flags);
422
423	list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
424			base.link) {
425		/* if event's pipe isn't same as crtc then ignore it. */
426		if (pipe != e->pipe)
427			continue;
428
429		list_del(&e->base.link);
430		drm_send_vblank_event(dev, -1, e);
431		drm_vblank_put(dev, pipe);
432		atomic_set(&exynos_crtc->pending_flip, 0);
433		wake_up(&exynos_crtc->pending_flip_queue);
434	}
435
436	spin_unlock_irqrestore(&dev->event_lock, flags);
437}
438
439void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
440			struct exynos_drm_overlay *overlay)
441{
442	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
443
444	if (manager->ops->win_mode_set)
445		manager->ops->win_mode_set(manager, overlay);
446}
447
448void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos)
449{
450	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
451
452	if (manager->ops->win_commit)
453		manager->ops->win_commit(manager, zpos);
454}
455
456void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos)
457{
458	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
459
460	if (manager->ops->win_enable)
461		manager->ops->win_enable(manager, zpos);
462}
463
464void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos)
465{
466	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
467
468	if (manager->ops->win_disable)
469		manager->ops->win_disable(manager, zpos);
470}
471
472void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
473{
474	struct exynos_drm_manager *manager;
475	struct drm_device *dev = fb->dev;
476	struct drm_crtc *crtc;
477
478	/*
479	 * make sure that overlay data are updated to real hardware
480	 * for all encoders.
481	 */
482	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
483		manager = to_exynos_crtc(crtc)->manager;
484
485		/*
486		 * wait for vblank interrupt
487		 * - this makes sure that overlay data are updated to
488		 *	real hardware.
489		 */
490		if (manager->ops->wait_for_vblank)
491			manager->ops->wait_for_vblank(manager);
492	}
493}