| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | /* exynos_drm_encoder.c
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2011 Samsung Electronics Co., Ltd. | 
					
						
							|  |  |  |  * Authors: | 
					
						
							|  |  |  |  *	Inki Dae <inki.dae@samsung.com> | 
					
						
							|  |  |  |  *	Joonyoung Shim <jy0922.shim@samsung.com> | 
					
						
							|  |  |  |  *	Seung-Woo Kim <sw0312.kim@samsung.com> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-12-18 02:30:17 +09:00
										 |  |  |  * This program is free software; you can redistribute  it and/or modify it | 
					
						
							|  |  |  |  * under  the terms of  the GNU General  Public License as published by the | 
					
						
							|  |  |  |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
					
						
							|  |  |  |  * option) any later version. | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-02 18:01:07 +01:00
										 |  |  | #include <drm/drmP.h>
 | 
					
						
							|  |  |  | #include <drm/drm_crtc_helper.h>
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "exynos_drm_drv.h"
 | 
					
						
							|  |  |  | #include "exynos_drm_encoder.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define to_exynos_encoder(x)	container_of(x, struct exynos_drm_encoder,\
 | 
					
						
							|  |  |  | 				drm_encoder) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * exynos specific encoder structure. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @drm_encoder: encoder object. | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  |  * @display: the display structure that maps to this encoder | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  |  */ | 
					
						
							|  |  |  | struct exynos_drm_encoder { | 
					
						
							|  |  |  | 	struct drm_encoder		drm_encoder; | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_display	*display; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 	struct exynos_drm_display *display = exynos_encoder->display; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-12 10:40:52 +09:00
										 |  |  | 	DRM_DEBUG_KMS("encoder dpms: %d\n", mode); | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	if (display->ops->dpms) | 
					
						
							|  |  |  | 		display->ops->dpms(display, mode); | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool | 
					
						
							|  |  |  | exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, | 
					
						
							| 
									
										
										
										
											2012-07-17 17:56:50 +02:00
										 |  |  | 			       const struct drm_display_mode *mode, | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 			       struct drm_display_mode *adjusted_mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-03-16 18:47:04 +09:00
										 |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 	struct exynos_drm_display *display = exynos_encoder->display; | 
					
						
							| 
									
										
										
										
											2012-03-16 18:47:04 +09:00
										 |  |  | 	struct drm_connector *connector; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 		if (connector->encoder != encoder) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (display->ops->mode_fixup) | 
					
						
							|  |  |  | 			display->ops->mode_fixup(display, connector, mode, | 
					
						
							|  |  |  | 					adjusted_mode); | 
					
						
							| 
									
										
										
										
											2012-03-16 18:47:04 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, | 
					
						
							|  |  |  | 					 struct drm_display_mode *mode, | 
					
						
							|  |  |  | 					 struct drm_display_mode *adjusted_mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-01-30 16:19:17 -05:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 	struct exynos_drm_display *display = exynos_encoder->display; | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 16:19:17 -05:00
										 |  |  | 	if (display->ops->mode_set) | 
					
						
							|  |  |  | 		display->ops->mode_set(display, adjusted_mode); | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* drm framework doesn't check NULL. */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void exynos_drm_encoder_commit(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_display *display = exynos_encoder->display; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	if (display->ops->dpms) | 
					
						
							|  |  |  | 		display->ops->dpms(display, DRM_MODE_DPMS_ON); | 
					
						
							| 
									
										
										
										
											2012-11-22 17:41:23 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	if (display->ops->commit) | 
					
						
							|  |  |  | 		display->ops->commit(display); | 
					
						
							| 
									
										
										
										
											2012-11-22 17:41:23 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-24 10:54:12 -07:00
										 |  |  | static void exynos_drm_encoder_disable(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_plane *plane; | 
					
						
							|  |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* all planes connected to this encoder should be also disabled. */ | 
					
						
							| 
									
										
										
										
											2014-04-01 15:22:31 -07:00
										 |  |  | 	drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) { | 
					
						
							| 
									
										
										
										
											2012-08-24 10:54:12 -07:00
										 |  |  | 		if (plane->crtc == encoder->crtc) | 
					
						
							|  |  |  | 			plane->funcs->disable_plane(plane); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { | 
					
						
							|  |  |  | 	.dpms		= exynos_drm_encoder_dpms, | 
					
						
							|  |  |  | 	.mode_fixup	= exynos_drm_encoder_mode_fixup, | 
					
						
							|  |  |  | 	.mode_set	= exynos_drm_encoder_mode_set, | 
					
						
							|  |  |  | 	.prepare	= exynos_drm_encoder_prepare, | 
					
						
							|  |  |  | 	.commit		= exynos_drm_encoder_commit, | 
					
						
							| 
									
										
										
										
											2012-08-24 10:54:12 -07:00
										 |  |  | 	.disable	= exynos_drm_encoder_disable, | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	drm_encoder_cleanup(encoder); | 
					
						
							|  |  |  | 	kfree(exynos_encoder); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct drm_encoder_funcs exynos_encoder_funcs = { | 
					
						
							|  |  |  | 	.destroy = exynos_drm_encoder_destroy, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-15 11:25:19 +09:00
										 |  |  | static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_encoder *clone; | 
					
						
							|  |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	struct exynos_drm_display *display = exynos_encoder->display; | 
					
						
							| 
									
										
										
										
											2012-02-15 11:25:19 +09:00
										 |  |  | 	unsigned int clone_mask = 0; | 
					
						
							|  |  |  | 	int cnt = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 		switch (display->type) { | 
					
						
							| 
									
										
										
										
											2012-02-15 11:25:19 +09:00
										 |  |  | 		case EXYNOS_DISPLAY_TYPE_LCD: | 
					
						
							|  |  |  | 		case EXYNOS_DISPLAY_TYPE_HDMI: | 
					
						
							| 
									
										
										
										
											2012-03-21 10:55:26 +09:00
										 |  |  | 		case EXYNOS_DISPLAY_TYPE_VIDI: | 
					
						
							| 
									
										
										
										
											2012-02-15 11:25:19 +09:00
										 |  |  | 			clone_mask |= (1 << (cnt++)); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return clone_mask; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exynos_drm_encoder_setup(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) | 
					
						
							|  |  |  | 		encoder->possible_clones = exynos_drm_encoder_clones(encoder); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | struct drm_encoder * | 
					
						
							|  |  |  | exynos_drm_encoder_create(struct drm_device *dev, | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 			   struct exynos_drm_display *display, | 
					
						
							| 
									
										
										
										
											2014-01-30 16:19:11 -05:00
										 |  |  | 			   unsigned long possible_crtcs) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	if (!possible_crtcs) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2013-08-19 19:04:55 +09:00
										 |  |  | 	if (!exynos_encoder) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	exynos_encoder->display = display; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 	encoder = &exynos_encoder->drm_encoder; | 
					
						
							|  |  |  | 	encoder->possible_crtcs = possible_crtcs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_encoder_init(dev, encoder, &exynos_encoder_funcs, | 
					
						
							|  |  |  | 			DRM_MODE_ENCODER_TMDS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DRM_DEBUG_KMS("encoder has been created\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return encoder; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-19 21:02:55 +09:00
										 |  |  | 	return to_exynos_encoder(encoder)->display; | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | } |