| 
									
										
										
										
											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"
 | 
					
						
							| 
									
										
										
										
											2012-08-20 21:29:25 +09:00
										 |  |  | #include "exynos_drm_connector.h"
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define to_exynos_encoder(x)	container_of(x, struct exynos_drm_encoder,\
 | 
					
						
							|  |  |  | 				drm_encoder) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * exynos specific encoder structure. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @drm_encoder: encoder object. | 
					
						
							|  |  |  |  * @manager: specific encoder has its own manager to control a hardware | 
					
						
							|  |  |  |  *	appropriately and we can access a hardware drawing on this manager. | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  |  * @dpms: store the encoder dpms value. | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  |  * @updated: indicate whether overlay data updating is needed or not. | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  |  */ | 
					
						
							|  |  |  | struct exynos_drm_encoder { | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | 	struct drm_crtc			*old_crtc; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 	struct drm_encoder		drm_encoder; | 
					
						
							|  |  |  | 	struct exynos_drm_manager	*manager; | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  | 	int				dpms; | 
					
						
							|  |  |  | 	bool				updated; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 21:29:25 +09:00
										 |  |  | static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							|  |  |  | 	struct drm_connector *connector; | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 
					
						
							| 
									
										
										
										
											2012-09-24 20:04:24 +09:00
										 |  |  | 		if (exynos_drm_best_encoder(connector) == encoder) { | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 			DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", | 
					
						
							|  |  |  | 					connector->base.id, mode); | 
					
						
							| 
									
										
										
										
											2012-08-20 21:29:25 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			exynos_drm_display_power(connector, mode); | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | 
					
						
							| 
									
										
										
										
											2011-11-14 15:20:49 +09:00
										 |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 	if (exynos_encoder->dpms == mode) { | 
					
						
							|  |  |  | 		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&dev->struct_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-14 15:20:49 +09:00
										 |  |  | 	switch (mode) { | 
					
						
							|  |  |  | 	case DRM_MODE_DPMS_ON: | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 		if (manager_ops && manager_ops->apply) | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  | 			if (!exynos_encoder->updated) | 
					
						
							|  |  |  | 				manager_ops->apply(manager->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 21:29:25 +09:00
										 |  |  | 		exynos_drm_connector_power(encoder, mode); | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 		exynos_encoder->dpms = mode; | 
					
						
							| 
									
										
										
										
											2011-11-14 15:20:49 +09:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case DRM_MODE_DPMS_STANDBY: | 
					
						
							|  |  |  | 	case DRM_MODE_DPMS_SUSPEND: | 
					
						
							|  |  |  | 	case DRM_MODE_DPMS_OFF: | 
					
						
							| 
									
										
										
										
											2012-08-20 21:29:25 +09:00
										 |  |  | 		exynos_drm_connector_power(encoder, mode); | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 		exynos_encoder->dpms = mode; | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  | 		exynos_encoder->updated = false; | 
					
						
							| 
									
										
										
										
											2011-11-14 15:20:49 +09:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		DRM_ERROR("unspecified mode %d\n", mode); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 	mutex_unlock(&dev->struct_mutex); | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 	struct drm_connector *connector; | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | 
					
						
							|  |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 
					
						
							|  |  |  | 		if (connector->encoder == encoder) | 
					
						
							|  |  |  | 			if (manager_ops && manager_ops->mode_fixup) | 
					
						
							|  |  |  | 				manager_ops->mode_fixup(manager->dev, connector, | 
					
						
							|  |  |  | 							mode, adjusted_mode); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | static void disable_plane_to_crtc(struct drm_device *dev, | 
					
						
							|  |  |  | 						struct drm_crtc *old_crtc, | 
					
						
							|  |  |  | 						struct drm_crtc *new_crtc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_plane *plane; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * if old_crtc isn't same as encoder->crtc then it means that | 
					
						
							|  |  |  | 	 * user changed crtc id to another one so the plane to old_crtc | 
					
						
							|  |  |  | 	 * should be disabled and plane->crtc should be set to new_crtc | 
					
						
							|  |  |  | 	 * (encoder->crtc) | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) { | 
					
						
							|  |  |  | 		if (plane->crtc == old_crtc) { | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * do not change below call order. | 
					
						
							|  |  |  | 			 * | 
					
						
							|  |  |  | 			 * plane->funcs->disable_plane call checks | 
					
						
							|  |  |  | 			 * if encoder->crtc is same as plane->crtc and if same | 
					
						
							|  |  |  | 			 * then overlay_ops->disable callback will be called | 
					
						
							|  |  |  | 			 * to diasble current hw overlay so plane->crtc should | 
					
						
							|  |  |  | 			 * have new_crtc because new_crtc was set to | 
					
						
							|  |  |  | 			 * encoder->crtc in advance. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			plane->crtc = new_crtc; | 
					
						
							|  |  |  | 			plane->funcs->disable_plane(plane); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, | 
					
						
							|  |  |  | 					 struct drm_display_mode *mode, | 
					
						
							|  |  |  | 					 struct drm_display_mode *adjusted_mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *dev = encoder->dev; | 
					
						
							|  |  |  | 	struct drm_connector *connector; | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | 	struct exynos_drm_manager *manager; | 
					
						
							|  |  |  | 	struct exynos_drm_manager_ops *manager_ops; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | 		if (connector->encoder == encoder) { | 
					
						
							|  |  |  | 			struct exynos_drm_encoder *exynos_encoder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (exynos_encoder->old_crtc != encoder->crtc && | 
					
						
							|  |  |  | 					exynos_encoder->old_crtc) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * disable a plane to old crtc and change | 
					
						
							|  |  |  | 				 * crtc of the plane to new one. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				disable_plane_to_crtc(dev, | 
					
						
							|  |  |  | 						exynos_encoder->old_crtc, | 
					
						
							|  |  |  | 						encoder->crtc); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			manager = exynos_drm_get_manager(encoder); | 
					
						
							|  |  |  | 			manager_ops = manager->ops; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 			if (manager_ops && manager_ops->mode_set) | 
					
						
							| 
									
										
										
										
											2012-03-16 18:47:04 +09:00
										 |  |  | 				manager_ops->mode_set(manager->dev, | 
					
						
							|  |  |  | 							adjusted_mode); | 
					
						
							| 
									
										
										
										
											2012-08-17 17:58:38 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			exynos_encoder->old_crtc = encoder->crtc; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = exynos_encoder->manager; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (manager_ops && manager_ops->commit) | 
					
						
							|  |  |  | 		manager_ops->commit(manager->dev); | 
					
						
							| 
									
										
										
										
											2012-10-18 18:59:55 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * this will avoid one issue that overlay data is updated to | 
					
						
							|  |  |  | 	 * real hardware two times. | 
					
						
							|  |  |  | 	 * And this variable will be used to check if the data was | 
					
						
							|  |  |  | 	 * already updated or not by exynos_drm_encoder_dpms function. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	exynos_encoder->updated = true; | 
					
						
							| 
									
										
										
										
											2012-11-14 20:41:35 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * In case of setcrtc, there is no way to update encoder's dpms | 
					
						
							|  |  |  | 	 * so update it here. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	exynos_encoder->dpms = DRM_MODE_DPMS_ON; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-22 17:41:23 +09:00
										 |  |  | void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder; | 
					
						
							| 
									
										
										
										
											2012-12-06 20:16:00 +05:30
										 |  |  | 	struct exynos_drm_manager_ops *ops; | 
					
						
							| 
									
										
										
										
											2012-11-22 17:41:23 +09:00
										 |  |  | 	struct drm_device *dev = fb->dev; | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * make sure that overlay data are updated to real hardware | 
					
						
							|  |  |  | 	 * for all encoders. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 
					
						
							|  |  |  | 		exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							| 
									
										
										
										
											2012-12-06 20:16:00 +05:30
										 |  |  | 		ops = exynos_encoder->manager->ops; | 
					
						
							| 
									
										
										
										
											2012-11-22 17:41:23 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * wait for vblank interrupt | 
					
						
							|  |  |  | 		 * - this makes sure that overlay data are updated to | 
					
						
							|  |  |  | 		 *	real hardware. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2012-12-06 20:16:00 +05:30
										 |  |  | 		if (ops->wait_for_vblank) | 
					
						
							|  |  |  | 			ops->wait_for_vblank(exynos_encoder->manager->dev); | 
					
						
							| 
									
										
										
										
											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. */ | 
					
						
							|  |  |  | 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) { | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exynos_encoder->manager->pipe = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 	struct exynos_drm_display_ops *display_ops = | 
					
						
							|  |  |  | 				exynos_encoder->manager->display_ops; | 
					
						
							|  |  |  | 	unsigned int clone_mask = 0; | 
					
						
							|  |  |  | 	int cnt = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { | 
					
						
							|  |  |  | 		switch (display_ops->type) { | 
					
						
							|  |  |  | 		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, | 
					
						
							|  |  |  | 			   struct exynos_drm_manager *manager, | 
					
						
							|  |  |  | 			   unsigned int possible_crtcs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!manager || !possible_crtcs) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!manager->dev) | 
					
						
							|  |  |  | 		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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | 	exynos_encoder->dpms = DRM_MODE_DPMS_OFF; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 	exynos_encoder->manager = manager; | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, | 
					
						
							|  |  |  | 			    void (*fn)(struct drm_encoder *, void *)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *dev = crtc->dev; | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							| 
									
										
										
										
											2011-11-04 17:04:45 +09:00
										 |  |  | 	struct exynos_drm_private *private = dev->dev_private; | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 
					
						
							| 
									
										
										
										
											2011-11-04 17:04:45 +09:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * if crtc is detached from encoder, check pipe, | 
					
						
							|  |  |  | 		 * otherwise check crtc attached to encoder | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (!encoder->crtc) { | 
					
						
							|  |  |  | 			manager = to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 			if (manager->pipe < 0 || | 
					
						
							|  |  |  | 					private->crtc[manager->pipe] != crtc) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (encoder->crtc != crtc) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		fn(encoder, data); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							|  |  |  | 	int crtc = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | 	if (manager->pipe != crtc) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (manager_ops->enable_vblank) | 
					
						
							|  |  |  | 		manager_ops->enable_vblank(manager->dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							|  |  |  | 	int crtc = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | 	if (manager->pipe != crtc) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (manager_ops->disable_vblank) | 
					
						
							|  |  |  | 		manager_ops->disable_vblank(manager->dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-06 11:06:54 +09:00
										 |  |  | void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = exynos_encoder->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_manager_ops *manager_ops = manager->ops; | 
					
						
							|  |  |  | 	int mode = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (manager_ops && manager_ops->dpms) | 
					
						
							|  |  |  | 		manager_ops->dpms(manager->dev, mode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * if this condition is ok then it means that the crtc is already | 
					
						
							|  |  |  | 	 * detached from encoder and last function for detaching is properly | 
					
						
							|  |  |  | 	 * done, so clear pipe from manager to prevent repeated call. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (mode > DRM_MODE_DPMS_ON) { | 
					
						
							|  |  |  | 		if (!encoder->crtc) | 
					
						
							|  |  |  | 			manager->pipe = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	int pipe = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * when crtc is detached from encoder, this pipe is used | 
					
						
							|  |  |  | 	 * to select manager operation | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	manager->pipe = pipe; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | 
					
						
							|  |  |  | 	struct exynos_drm_overlay *overlay = data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-04 17:31:41 +09:00
										 |  |  | 	if (overlay_ops && overlay_ops->mode_set) | 
					
						
							|  |  |  | 		overlay_ops->mode_set(manager->dev, overlay); | 
					
						
							| 
									
										
										
										
											2011-10-04 19:19:01 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) | 
					
						
							| 
									
										
										
										
											2011-11-04 17:04:45 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | 
					
						
							| 
									
										
										
										
											2011-12-08 17:54:07 +09:00
										 |  |  | 	int zpos = DEFAULT_ZPOS; | 
					
						
							| 
									
										
										
										
											2011-11-04 17:04:45 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-08 17:54:07 +09:00
										 |  |  | 	if (data) | 
					
						
							|  |  |  | 		zpos = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | 	if (overlay_ops && overlay_ops->commit) | 
					
						
							|  |  |  | 		overlay_ops->commit(manager->dev, zpos); | 
					
						
							| 
									
										
										
										
											2011-11-04 17:04:45 +09:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:09 +09:00
										 |  |  | void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							|  |  |  | 	struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | 
					
						
							|  |  |  | 	int zpos = DEFAULT_ZPOS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (data) | 
					
						
							|  |  |  | 		zpos = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (overlay_ops && overlay_ops->enable) | 
					
						
							|  |  |  | 		overlay_ops->enable(manager->dev, zpos); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct exynos_drm_manager *manager = | 
					
						
							|  |  |  | 		to_exynos_encoder(encoder)->manager; | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | 	struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | 
					
						
							|  |  |  | 	int zpos = DEFAULT_ZPOS; | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:05 +09:00
										 |  |  | 	if (data) | 
					
						
							|  |  |  | 		zpos = *(int *)data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (overlay_ops && overlay_ops->disable) | 
					
						
							|  |  |  | 		overlay_ops->disable(manager->dev, zpos); | 
					
						
							| 
									
										
										
										
											2012-06-27 14:27:02 +09:00
										 |  |  | } |