1/* 2 * Copyright (C) STMicroelectronics SA 2014 3 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. 4 * License terms: GNU General Public License (GPL), version 2 5 */ 6 7#include "sti_hdmi_tx3g0c55phy.h" 8 9#define HDMI_SRZ_PLL_CFG 0x0504 10#define HDMI_SRZ_TAP_1 0x0508 11#define HDMI_SRZ_TAP_2 0x050C 12#define HDMI_SRZ_TAP_3 0x0510 13#define HDMI_SRZ_CTRL 0x0514 14 15#define HDMI_SRZ_PLL_CFG_POWER_DOWN BIT(0) 16#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT 1 17#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ 0 18#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ 1 19#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ 2 20#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ 3 21#define HDMI_SRZ_PLL_CFG_VCOR_MASK 3 22#define HDMI_SRZ_PLL_CFG_VCOR(x) (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT) 23#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT 8 24#define HDMI_SRZ_PLL_CFG_NDIV_MASK (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT) 25#define HDMI_SRZ_PLL_CFG_MODE_SHIFT 16 26#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ 0x1 27#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ 0x4 28#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ 0x5 29#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6 30#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ 0x7 31#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ 0x8 32#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ 0x9 33#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA 34#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ 0xB 35#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ 0xC 36#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ 0xD 37#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE 38#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ 0xF 39#define HDMI_SRZ_PLL_CFG_MODE_MASK 0xF 40#define HDMI_SRZ_PLL_CFG_MODE(x) (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT) 41 42#define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0) 43#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1) 44 45/* sysconf registers */ 46#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */ 47#define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */ 48 49#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0 50#define REJECTION_PLL_HDMI_ENABLE_MASK (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT) 51#define REJECTION_PLL_HDMI_PDIV_SHIFT 24 52#define REJECTION_PLL_HDMI_PDIV_MASK (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT) 53#define REJECTION_PLL_HDMI_NDIV_SHIFT 16 54#define REJECTION_PLL_HDMI_NDIV_MASK (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT) 55#define REJECTION_PLL_HDMI_MDIV_SHIFT 8 56#define REJECTION_PLL_HDMI_MDIV_MASK (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT) 57 58#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0) 59 60#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ 61 62/** 63 * pll mode structure 64 * 65 * A pointer to an array of these structures is passed to a TMDS (HDMI) output 66 * via the control interface to provide board and SoC specific 67 * configurations of the HDMI PHY. Each entry in the array specifies a hardware 68 * specific configuration for a given TMDS clock frequency range. The array 69 * should be terminated with an entry that has all fields set to zero. 70 * 71 * @min: Lower bound of TMDS clock frequency this entry applies to 72 * @max: Upper bound of TMDS clock frequency this entry applies to 73 * @mode: SoC specific register configuration 74 */ 75struct pllmode { 76 u32 min; 77 u32 max; 78 u32 mode; 79}; 80 81#define NB_PLL_MODE 7 82static struct pllmode pllmodes[NB_PLL_MODE] = { 83 {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ}, 84 {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ}, 85 {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ}, 86 {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ}, 87 {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ}, 88 {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ}, 89 {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ} 90}; 91 92#define NB_HDMI_PHY_CONFIG 5 93static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { 94 {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} }, 95 {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} }, 96 {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} }, 97 {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} }, 98 {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} }, 99}; 100 101#define PLL_CHANGE_DELAY 1 /* ms */ 102 103/** 104 * Disable the pll rejection 105 * 106 * @hdmi: pointer on the hdmi internal structure 107 * 108 * return true if the pll has been disabled 109 */ 110static bool disable_pll_rejection(struct sti_hdmi *hdmi) 111{ 112 u32 val; 113 114 DRM_DEBUG_DRIVER("\n"); 115 116 val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); 117 val &= ~REJECTION_PLL_HDMI_ENABLE_MASK; 118 writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); 119 120 msleep(PLL_CHANGE_DELAY); 121 val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); 122 123 return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); 124} 125 126/** 127 * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL 128 * clock input to the new PHY PLL that generates the serializer clock 129 * (TMDS*10) and the TMDS clock which is now fed back into the HDMI 130 * formatter instead of the TMDS clock line from ClockGenB. 131 * 132 * @hdmi: pointer on the hdmi internal structure 133 * 134 * return true if pll has been correctly set 135 */ 136static bool enable_pll_rejection(struct sti_hdmi *hdmi) 137{ 138 unsigned int inputclock; 139 u32 mdiv, ndiv, pdiv, val; 140 141 DRM_DEBUG_DRIVER("\n"); 142 143 if (!disable_pll_rejection(hdmi)) 144 return false; 145 146 inputclock = hdmi->mode.clock * 1000; 147 148 DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock); 149 150 151 /* Power up the HDMI rejection PLL 152 * Note: On this SoC (stiH416) we are forced to have the input clock 153 * be equal to the HDMI pixel clock. 154 * 155 * The values here have been suggested by validation however they are 156 * still provisional and subject to change. 157 * 158 * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv) 159 */ 160 if (inputclock < 50000000) { 161 /* 162 * For slower clocks we need to multiply more to keep the 163 * internal VCO frequency within the physical specification 164 * of the PLL. 165 */ 166 pdiv = 4; 167 ndiv = 240; 168 mdiv = 30; 169 } else { 170 pdiv = 2; 171 ndiv = 60; 172 mdiv = 30; 173 } 174 175 val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); 176 177 val &= ~(REJECTION_PLL_HDMI_PDIV_MASK | 178 REJECTION_PLL_HDMI_NDIV_MASK | 179 REJECTION_PLL_HDMI_MDIV_MASK | 180 REJECTION_PLL_HDMI_ENABLE_MASK); 181 182 val |= (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) | 183 (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) | 184 (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) | 185 (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT); 186 187 writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); 188 189 msleep(PLL_CHANGE_DELAY); 190 val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); 191 192 return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); 193} 194 195/** 196 * Start hdmi phy macro cell tx3g0c55 197 * 198 * @hdmi: pointer on the hdmi internal structure 199 * 200 * Return false if an error occur 201 */ 202static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi) 203{ 204 u32 ckpxpll = hdmi->mode.clock * 1000; 205 u32 val, tmdsck, freqvco, pllctrl = 0; 206 unsigned int i; 207 208 if (!enable_pll_rejection(hdmi)) 209 return false; 210 211 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); 212 213 /* Assuming no pixel repetition and 24bits color */ 214 tmdsck = ckpxpll; 215 pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT; 216 217 /* 218 * Setup the PLL mode parameter based on the ckpxpll. If we haven't got 219 * a clock frequency supported by one of the specific PLL modes then we 220 * will end up using the generic mode (0) which only supports a 10x 221 * multiplier, hence only 24bit color. 222 */ 223 for (i = 0; i < NB_PLL_MODE; i++) { 224 if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max) 225 pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode); 226 } 227 228 freqvco = tmdsck * 10; 229 if (freqvco <= 425000000UL) 230 pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ); 231 else if (freqvco <= 850000000UL) 232 pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ); 233 else if (freqvco <= 1700000000UL) 234 pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ); 235 else if (freqvco <= 2970000000UL) 236 pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ); 237 else { 238 DRM_ERROR("PHY serializer clock out of range\n"); 239 goto err; 240 } 241 242 /* 243 * Configure and power up the PHY PLL 244 */ 245 hdmi->event_received = false; 246 DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); 247 hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG); 248 249 /* wait PLL interrupt */ 250 wait_event_interruptible_timeout(hdmi->wait_event, 251 hdmi->event_received == true, 252 msecs_to_jiffies 253 (HDMI_TIMEOUT_PLL_LOCK)); 254 255 if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { 256 DRM_ERROR("hdmi phy pll not locked\n"); 257 goto err; 258 } 259 260 DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); 261 262 /* 263 * To configure the source termination and pre-emphasis appropriately 264 * for different high speed TMDS clock frequencies a phy configuration 265 * table must be provided, tailored to the SoC and board combination. 266 */ 267 for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { 268 if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && 269 (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { 270 val = hdmiphy_config[i].config[0]; 271 hdmi_write(hdmi, val, HDMI_SRZ_TAP_1); 272 val = hdmiphy_config[i].config[1]; 273 hdmi_write(hdmi, val, HDMI_SRZ_TAP_2); 274 val = hdmiphy_config[i].config[2]; 275 hdmi_write(hdmi, val, HDMI_SRZ_TAP_3); 276 val = hdmiphy_config[i].config[3]; 277 val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN; 278 val &= ~HDMI_SRZ_CTRL_POWER_DOWN; 279 hdmi_write(hdmi, val, HDMI_SRZ_CTRL); 280 281 DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n", 282 hdmiphy_config[i].config[0], 283 hdmiphy_config[i].config[1], 284 hdmiphy_config[i].config[2], 285 hdmiphy_config[i].config[3]); 286 return true; 287 } 288 } 289 290 /* 291 * Default, power up the serializer with no pre-emphasis or source 292 * termination. 293 */ 294 hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1); 295 hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2); 296 hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3); 297 hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL); 298 299 return true; 300 301err: 302 disable_pll_rejection(hdmi); 303 304 return false; 305} 306 307/** 308 * Stop hdmi phy macro cell tx3g0c55 309 * 310 * @hdmi: pointer on the hdmi internal structure 311 */ 312static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi) 313{ 314 DRM_DEBUG_DRIVER("\n"); 315 316 hdmi->event_received = false; 317 318 hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL); 319 hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG); 320 321 /* wait PLL interrupt */ 322 wait_event_interruptible_timeout(hdmi->wait_event, 323 hdmi->event_received == true, 324 msecs_to_jiffies 325 (HDMI_TIMEOUT_PLL_LOCK)); 326 327 if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) 328 DRM_ERROR("hdmi phy pll not well disabled\n"); 329 330 disable_pll_rejection(hdmi); 331} 332 333struct hdmi_phy_ops tx3g0c55phy_ops = { 334 .start = sti_hdmi_tx3g0c55phy_start, 335 .stop = sti_hdmi_tx3g0c55phy_stop, 336}; 337