215 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			215 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Copyright (C) 2013 Red Hat | ||
|  |  * Author: Rob Clark <robdclark@gmail.com> | ||
|  |  * | ||
|  |  * This program is free software; you can redistribute it and/or modify it | ||
|  |  * under the terms of the GNU General Public License version 2 as published by | ||
|  |  * the Free Software Foundation. | ||
|  |  * | ||
|  |  * This program is distributed in the hope that it will be useful, but WITHOUT | ||
|  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
|  |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||
|  |  * more details. | ||
|  |  * | ||
|  |  * You should have received a copy of the GNU General Public License along with | ||
|  |  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "hdmi.h"
 | ||
|  | 
 | ||
|  | struct hdmi_phy_8x60 { | ||
|  | 	struct hdmi_phy base; | ||
|  | 	struct hdmi *hdmi; | ||
|  | }; | ||
|  | #define to_hdmi_phy_8x60(x) container_of(x, struct hdmi_phy_8x60, base)
 | ||
|  | 
 | ||
|  | static void hdmi_phy_8x60_destroy(struct hdmi_phy *phy) | ||
|  | { | ||
|  | 	struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); | ||
|  | 	kfree(phy_8x60); | ||
|  | } | ||
|  | 
 | ||
|  | static void hdmi_phy_8x60_reset(struct hdmi_phy *phy) | ||
|  | { | ||
|  | 	struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); | ||
|  | 	struct hdmi *hdmi = phy_8x60->hdmi; | ||
|  | 	unsigned int val; | ||
|  | 
 | ||
|  | 	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); | ||
|  | 
 | ||
|  | 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { | ||
|  | 		/* pull low */ | ||
|  | 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, | ||
|  | 				val & ~HDMI_PHY_CTRL_SW_RESET); | ||
|  | 	} else { | ||
|  | 		/* pull high */ | ||
|  | 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, | ||
|  | 				val | HDMI_PHY_CTRL_SW_RESET); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	msleep(100); | ||
|  | 
 | ||
|  | 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { | ||
|  | 		/* pull high */ | ||
|  | 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, | ||
|  | 				val | HDMI_PHY_CTRL_SW_RESET); | ||
|  | 	} else { | ||
|  | 		/* pull low */ | ||
|  | 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, | ||
|  | 				val & ~HDMI_PHY_CTRL_SW_RESET); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy, | ||
|  | 		unsigned long int pixclock) | ||
|  | { | ||
|  | 	struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); | ||
|  | 	struct hdmi *hdmi = phy_8x60->hdmi; | ||
|  | 
 | ||
|  | 	/* De-serializer delay D/C for non-lbk mode: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG0, | ||
|  | 			HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3)); | ||
|  | 
 | ||
|  | 	if (pixclock == 27000000) { | ||
|  | 		/* video_format == HDMI_VFRMT_720x480p60_16_9 */ | ||
|  | 		hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1, | ||
|  | 				HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | | ||
|  | 				HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3)); | ||
|  | 	} else { | ||
|  | 		hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1, | ||
|  | 				HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | | ||
|  | 				HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* No matter what, start from the power down mode: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_PD_PWRGEN | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_PLL | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_4 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_3 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_2 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_1 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 
 | ||
|  | 	/* Turn PowerGen on: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_PD_PLL | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_4 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_3 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_2 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_1 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 
 | ||
|  | 	/* Turn PLL power on: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_4 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_3 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_2 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_1 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 
 | ||
|  | 	/* Write to HIGH after PLL power down de-assert: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, | ||
|  | 			HDMI_8x60_PHY_REG3_PLL_ENABLE); | ||
|  | 
 | ||
|  | 	/* ASIC power on; PHY REG9 = 0 */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0); | ||
|  | 
 | ||
|  | 	/* Enable PLL lock detect, PLL lock det will go high after lock
 | ||
|  | 	 * Enable the re-time logic | ||
|  | 	 */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12, | ||
|  | 			HDMI_8x60_PHY_REG12_RETIMING_EN | | ||
|  | 			HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN); | ||
|  | 
 | ||
|  | 	/* Drivers are on: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 
 | ||
|  | 	/* If the RX detector is needed: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_RCV_SENSE_EN | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 
 | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG4, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG5, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG6, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG7, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG8, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG10, 0); | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG11, 0); | ||
|  | 
 | ||
|  | 	/* If we want to use lock enable based on counting: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12, | ||
|  | 			HDMI_8x60_PHY_REG12_RETIMING_EN | | ||
|  | 			HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN | | ||
|  | 			HDMI_8x60_PHY_REG12_FORCE_LOCK); | ||
|  | } | ||
|  | 
 | ||
|  | static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy) | ||
|  | { | ||
|  | 	struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); | ||
|  | 	struct hdmi *hdmi = phy_8x60->hdmi; | ||
|  | 
 | ||
|  | 	/* Assert RESET PHY from controller */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_PHY_CTRL, | ||
|  | 			HDMI_PHY_CTRL_SW_RESET); | ||
|  | 	udelay(10); | ||
|  | 	/* De-assert RESET PHY from controller */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_PHY_CTRL, 0); | ||
|  | 	/* Turn off Driver */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_4 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_3 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_2 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_1 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | 	udelay(10); | ||
|  | 	/* Disable PLL */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, 0); | ||
|  | 	/* Power down PHY, but keep RX-sense: */ | ||
|  | 	hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, | ||
|  | 			HDMI_8x60_PHY_REG2_RCV_SENSE_EN | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_PWRGEN | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_PLL | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_4 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_3 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_2 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DRIVE_1 | | ||
|  | 			HDMI_8x60_PHY_REG2_PD_DESER); | ||
|  | } | ||
|  | 
 | ||
|  | static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = { | ||
|  | 		.destroy = hdmi_phy_8x60_destroy, | ||
|  | 		.reset = hdmi_phy_8x60_reset, | ||
|  | 		.powerup = hdmi_phy_8x60_powerup, | ||
|  | 		.powerdown = hdmi_phy_8x60_powerdown, | ||
|  | }; | ||
|  | 
 | ||
|  | struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi) | ||
|  | { | ||
|  | 	struct hdmi_phy_8x60 *phy_8x60; | ||
|  | 	struct hdmi_phy *phy = NULL; | ||
|  | 	int ret; | ||
|  | 
 | ||
|  | 	phy_8x60 = kzalloc(sizeof(*phy_8x60), GFP_KERNEL); | ||
|  | 	if (!phy_8x60) { | ||
|  | 		ret = -ENOMEM; | ||
|  | 		goto fail; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	phy = &phy_8x60->base; | ||
|  | 
 | ||
|  | 	phy->funcs = &hdmi_phy_8x60_funcs; | ||
|  | 
 | ||
|  | 	phy_8x60->hdmi = hdmi; | ||
|  | 
 | ||
|  | 	return phy; | ||
|  | 
 | ||
|  | fail: | ||
|  | 	if (phy) | ||
|  | 		hdmi_phy_8x60_destroy(phy); | ||
|  | 	return ERR_PTR(ret); | ||
|  | } |