1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) STMicroelectronics SA 2014 4 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. 5 */ 6 7 #include "sti_hdmi_tx3g4c28phy.h" 8 9 #define HDMI_SRZ_CFG 0x504 10 #define HDMI_SRZ_PLL_CFG 0x510 11 #define HDMI_SRZ_ICNTL 0x518 12 #define HDMI_SRZ_CALCODE_EXT 0x520 13 14 #define HDMI_SRZ_CFG_EN BIT(0) 15 #define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1) 16 #define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16) 17 #define HDMI_SRZ_CFG_RBIAS_EXT BIT(17) 18 #define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18) 19 #define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19) 20 #define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24) 21 22 #define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \ 23 HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \ 24 HDMI_SRZ_CFG_EXTERNAL_DATA | \ 25 HDMI_SRZ_CFG_RBIAS_EXT | \ 26 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \ 27 HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \ 28 HDMI_SRZ_CFG_EN_SRC_TERMINATION) 29 30 #define PLL_CFG_EN BIT(0) 31 #define PLL_CFG_NDIV_SHIFT (8) 32 #define PLL_CFG_IDF_SHIFT (16) 33 #define PLL_CFG_ODF_SHIFT (24) 34 35 #define ODF_DIV_1 (0) 36 #define ODF_DIV_2 (1) 37 #define ODF_DIV_4 (2) 38 #define ODF_DIV_8 (3) 39 40 #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ 41 42 struct plldividers_s { 43 uint32_t min; 44 uint32_t max; 45 uint32_t idf; 46 uint32_t odf; 47 }; 48 49 /* 50 * Functional specification recommended values 51 */ 52 #define NB_PLL_MODE 5 53 static struct plldividers_s plldividers[NB_PLL_MODE] = { 54 {0, 20000000, 1, ODF_DIV_8}, 55 {20000000, 42500000, 2, ODF_DIV_8}, 56 {42500000, 85000000, 4, ODF_DIV_4}, 57 {85000000, 170000000, 8, ODF_DIV_2}, 58 {170000000, 340000000, 16, ODF_DIV_1} 59 }; 60 61 #define NB_HDMI_PHY_CONFIG 2 62 static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { 63 {0, 250000000, {0x0, 0x0, 0x0, 0x0} }, 64 {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} }, 65 }; 66 67 /** 68 * Start hdmi phy macro cell tx3g4c28 69 * 70 * @hdmi: pointer on the hdmi internal structure 71 * 72 * Return false if an error occur 73 */ 74 static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi) 75 { 76 u32 ckpxpll = hdmi->mode.clock * 1000; 77 u32 val, tmdsck, idf, odf, pllctrl = 0; 78 bool foundplldivides = false; 79 int i; 80 81 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); 82 83 for (i = 0; i < NB_PLL_MODE; i++) { 84 if (ckpxpll >= plldividers[i].min && 85 ckpxpll < plldividers[i].max) { 86 idf = plldividers[i].idf; 87 odf = plldividers[i].odf; 88 foundplldivides = true; 89 break; 90 } 91 } 92 93 if (!foundplldivides) { 94 DRM_ERROR("input TMDS clock speed (%d) not supported\n", 95 ckpxpll); 96 goto err; 97 } 98 99 /* Assuming no pixel repetition and 24bits color */ 100 tmdsck = ckpxpll; 101 pllctrl |= 40 << PLL_CFG_NDIV_SHIFT; 102 103 if (tmdsck > 340000000) { 104 DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck); 105 goto err; 106 } 107 108 pllctrl |= idf << PLL_CFG_IDF_SHIFT; 109 pllctrl |= odf << PLL_CFG_ODF_SHIFT; 110 111 /* 112 * Configure and power up the PHY PLL 113 */ 114 hdmi->event_received = false; 115 DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); 116 hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG); 117 118 /* wait PLL interrupt */ 119 wait_event_interruptible_timeout(hdmi->wait_event, 120 hdmi->event_received == true, 121 msecs_to_jiffies 122 (HDMI_TIMEOUT_PLL_LOCK)); 123 124 if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { 125 DRM_ERROR("hdmi phy pll not locked\n"); 126 goto err; 127 } 128 129 DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); 130 131 val = (HDMI_SRZ_CFG_EN | 132 HDMI_SRZ_CFG_EXTERNAL_DATA | 133 HDMI_SRZ_CFG_EN_BIASRES_DETECTION | 134 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION); 135 136 if (tmdsck > 165000000) 137 val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION; 138 139 /* 140 * To configure the source termination and pre-emphasis appropriately 141 * for different high speed TMDS clock frequencies a phy configuration 142 * table must be provided, tailored to the SoC and board combination. 143 */ 144 for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { 145 if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && 146 (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { 147 val |= (hdmiphy_config[i].config[0] 148 & ~HDMI_SRZ_CFG_INTERNAL_MASK); 149 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 150 151 val = hdmiphy_config[i].config[1]; 152 hdmi_write(hdmi, val, HDMI_SRZ_ICNTL); 153 154 val = hdmiphy_config[i].config[2]; 155 hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT); 156 157 DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n", 158 hdmiphy_config[i].config[0], 159 hdmiphy_config[i].config[1], 160 hdmiphy_config[i].config[2]); 161 return true; 162 } 163 } 164 165 /* 166 * Default, power up the serializer with no pre-emphasis or 167 * output swing correction 168 */ 169 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 170 hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL); 171 hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT); 172 173 return true; 174 175 err: 176 return false; 177 } 178 179 /** 180 * Stop hdmi phy macro cell tx3g4c28 181 * 182 * @hdmi: pointer on the hdmi internal structure 183 */ 184 static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi) 185 { 186 int val = 0; 187 188 DRM_DEBUG_DRIVER("\n"); 189 190 hdmi->event_received = false; 191 192 val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION; 193 val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION; 194 195 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 196 hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG); 197 198 /* wait PLL interrupt */ 199 wait_event_interruptible_timeout(hdmi->wait_event, 200 hdmi->event_received == true, 201 msecs_to_jiffies 202 (HDMI_TIMEOUT_PLL_LOCK)); 203 204 if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) 205 DRM_ERROR("hdmi phy pll not well disabled\n"); 206 } 207 208 struct hdmi_phy_ops tx3g4c28phy_ops = { 209 .start = sti_hdmi_tx3g4c28phy_start, 210 .stop = sti_hdmi_tx3g4c28phy_stop, 211 }; 212