1 /* $OpenBSD: dwhdmiphy.c,v 1.1 2020/03/02 10:37:23 kettenis Exp $ */ 2 /* $NetBSD: dw_hdmi_phy.c,v 1.2 2019/11/10 10:36:01 jmcneill Exp $ */ 3 4 /*- 5 * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 32 #include <drm/drmP.h> 33 34 #include <dev/ic/dwhdmi.h> 35 36 #define HDMI_IH_PHY_STAT0 0x0104 37 #define HDMI_IH_PHY_STAT0_HPD (1 << 0) 38 #define HDMI_IH_I2CMPHY_STAT0 0x0108 39 #define HDMI_IH_I2CMPHY_STAT0_DONE (1 << 1) 40 #define HDMI_IH_I2CMPHY_STAT0_ERROR (1 << 0) 41 42 #define HDMI_PHY_CONF0 0x3000 43 #define HDMI_PHY_CONF0_PDZ_MASK 0x80 44 #define HDMI_PHY_CONF0_PDZ_OFFSET 7 45 #define HDMI_PHY_CONF0_ENTMDS_MASK 0x40 46 #define HDMI_PHY_CONF0_ENTMDS_OFFSET 6 47 #define HDMI_PHY_CONF0_SVSRET_MASK 0x20 48 #define HDMI_PHY_CONF0_SVSRET_OFFSET 5 49 #define HDMI_PHY_CONF0_GEN2_PDDQ_MASK 0x10 50 #define HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET 4 51 #define HDMI_PHY_CONF0_GEN2_TXPWRON_MASK 0x8 52 #define HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET 3 53 #define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK 0x4 54 #define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET 2 55 #define HDMI_PHY_CONF0_SELDATAENPOL_MASK 0x2 56 #define HDMI_PHY_CONF0_SELDATAENPOL_OFFSET 1 57 #define HDMI_PHY_CONF0_SELDIPIF_MASK 0x1 58 #define HDMI_PHY_CONF0_SELDIPIF_OFFSET 0 59 #define HDMI_PHY_TST0 0x3001 60 #define HDMI_PHY_TST0_TSTCLR_MASK 0x20 61 #define HDMI_PHY_TST0_TSTCLR_OFFSET 5 62 #define HDMI_PHY_TST0_TSTEN_MASK 0x10 63 #define HDMI_PHY_TST0_TSTEN_OFFSET 4 64 #define HDMI_PHY_TST0_TSTCLK_MASK 0x1 65 #define HDMI_PHY_TST0_TSTCLK_OFFSET 0 66 #define HDMI_PHY_TST1 0x3002 67 #define HDMI_PHY_TST2 0x3003 68 #define HDMI_PHY_STAT0 0x3004 69 #define HDMI_PHY_STAT0_RX_SENSE3 0x80 70 #define HDMI_PHY_STAT0_RX_SENSE2 0x40 71 #define HDMI_PHY_STAT0_RX_SENSE1 0x20 72 #define HDMI_PHY_STAT0_RX_SENSE0 0x10 73 #define HDMI_PHY_STAT0_RX_SENSE 0xf0 74 #define HDMI_PHY_STAT0_HPD 0x02 75 #define HDMI_PHY_TX_PHY_LOCK 0x01 76 #define HDMI_PHY_INT0 0x3005 77 #define HDMI_PHY_MASK0 0x3006 78 #define HDMI_PHY_POL0 0x3007 79 #define HDMI_PHY_POL0_HPD 0x02 80 81 /* HDMI Master PHY Registers */ 82 #define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 83 #define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69 84 #define HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY 0x49 85 #define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 86 #define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 87 #define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 88 #define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 89 #define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 90 #define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 91 #define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10 92 #define HDMI_PHY_I2CM_OPERATION_ADDR_READ 0x1 93 #define HDMI_PHY_I2CM_INT_ADDR 0x3027 94 #define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 95 #define HDMI_PHY_I2CM_DIV_ADDR 0x3029 96 #define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a 97 #define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b 98 #define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c 99 #define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d 100 #define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e 101 #define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f 102 #define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 103 #define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 104 #define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 105 106 #define HDMI_MC_FLOWCTRL 0x4004 107 #define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK 0x1 108 #define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH 0x1 109 #define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS 0x0 110 #define HDMI_MC_PHYRSTZ 0x4005 111 #define HDMI_MC_PHYRSTZ_ASSERT 0x0 112 #define HDMI_MC_PHYRSTZ_DEASSERT 0x1 113 #define HDMI_MC_HEACPHY_RST 0x4007 114 #define HDMI_MC_HEACPHY_RST_ASSERT 0x1 115 #define HDMI_MC_HEACPHY_RST_DEASSERT 0x0 116 117 /* HDMI PHY register with access through I2C */ 118 #define HDMI_PHY_I2C_CKCALCTRL 0x5 119 #define CKCALCTRL_OVERRIDE (1 << 15) 120 #define HDMI_PHY_I2C_CPCE_CTRL 0x6 121 #define CPCE_CTRL_45_25 ((3 << 7) | (3 << 5)) 122 #define CPCE_CTRL_92_50 ((2 << 7) | (2 << 5)) 123 #define CPCE_CTRL_185 ((1 << 7) | (1 << 5)) 124 #define CPCE_CTRL_370 ((0 << 7) | (0 << 5)) 125 #define HDMI_PHY_I2C_CKSYMTXCTRL 0x9 126 #define CKSYMTXCTRL_OVERRIDE (1 << 15) 127 #define CKSYMTXCTRL_TX_SYMON (1 << 3) 128 #define CKSYMTXCTRL_TX_TRAON (1 << 2) 129 #define CKSYMTXCTRL_TX_TRBON (1 << 1) 130 #define CKSYMTXCTRL_TX_CK_SYMON (1 << 0) 131 #define HDMI_PHY_I2C_VLEVCTRL 0x0E 132 #define HDMI_PHY_I2C_CURRCTRL 0x10 133 #define HDMI_PHY_I2C_PLLPHBYCTRL 0x13 134 #define VLEVCTRL_TX_LVL(x) ((x) << 5) 135 #define VLEVCTRL_CK_LVL(x) (x) 136 #define HDMI_PHY_I2C_GMPCTRL 0x15 137 #define GMPCTRL_45_25 0x00 138 #define GMPCTRL_92_50 0x05 139 #define GMPCTRL_185 0x0a 140 #define GMPCTRL_370 0x0f 141 #define HDMI_PHY_I2C_MSM_CTRL 0x17 142 #define MSM_CTRL_FB_CLK (0x3 << 1) 143 #define HDMI_PHY_I2C_TXTERM 0x19 144 #define TXTERM_133 0x5 145 146 void 147 dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc *sc, int msec) 148 { 149 uint8_t val; 150 151 val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & 152 (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); 153 while (val == 0) { 154 delay(1000); 155 msec -= 10; 156 if (msec <= 0) 157 return; 158 val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & 159 (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); 160 } 161 } 162 163 void 164 dwhdmi_phy_i2c_write(struct dwhdmi_softc *sc, unsigned short data, 165 unsigned char addr) 166 { 167 168 /* clear DONE and ERROR flags */ 169 dwhdmi_write(sc, HDMI_IH_I2CMPHY_STAT0, 170 HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); 171 dwhdmi_write(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr); 172 dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff)); 173 dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff)); 174 dwhdmi_write(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE); 175 dwhdmi_phy_wait_i2c_done(sc, 1000); 176 } 177 178 void 179 dwhdmi_phy_enable_power(struct dwhdmi_softc *sc, uint8_t enable) 180 { 181 uint8_t reg; 182 183 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 184 reg &= ~HDMI_PHY_CONF0_PDZ_MASK; 185 reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET); 186 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 187 } 188 189 void 190 dwhdmi_phy_enable_tmds(struct dwhdmi_softc *sc, uint8_t enable) 191 { 192 uint8_t reg; 193 194 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 195 reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK; 196 reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET); 197 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 198 } 199 200 void 201 dwhdmi_phy_gen2_pddq(struct dwhdmi_softc *sc, uint8_t enable) 202 { 203 uint8_t reg; 204 205 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 206 reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; 207 reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET); 208 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 209 } 210 211 void 212 dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc *sc, uint8_t enable) 213 { 214 uint8_t reg; 215 216 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 217 reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; 218 reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET); 219 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 220 } 221 222 void 223 dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc *sc, uint8_t enable) 224 { 225 uint8_t reg; 226 227 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 228 reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK; 229 reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET); 230 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 231 } 232 233 void 234 dwhdmi_phy_sel_interface_control(struct dwhdmi_softc *sc, uint8_t enable) 235 { 236 uint8_t reg; 237 238 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 239 reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK; 240 reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET); 241 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 242 } 243 244 void 245 dwhdmi_phy_enable_svsret(struct dwhdmi_softc *sc, uint8_t enable) 246 { 247 uint8_t reg; 248 249 reg = dwhdmi_read(sc, HDMI_PHY_CONF0); 250 reg &= ~HDMI_PHY_CONF0_SVSRET_MASK; 251 reg |= (enable << HDMI_PHY_CONF0_SVSRET_OFFSET); 252 dwhdmi_write(sc, HDMI_PHY_CONF0, reg); 253 } 254 255 void 256 dwhdmi_phy_test_clear(struct dwhdmi_softc *sc, unsigned char bit) 257 { 258 uint8_t val; 259 260 val = dwhdmi_read(sc, HDMI_PHY_TST0); 261 val &= ~HDMI_PHY_TST0_TSTCLR_MASK; 262 val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & 263 HDMI_PHY_TST0_TSTCLR_MASK; 264 dwhdmi_write(sc, HDMI_PHY_TST0, val); 265 } 266 267 int 268 dwhdmi_phy_configure(struct dwhdmi_softc *sc, struct drm_display_mode *mode) 269 { 270 const struct dwhdmi_mpll_config *mpll_conf; 271 const struct dwhdmi_phy_config *phy_conf; 272 uint8_t val; 273 uint8_t msec; 274 275 dwhdmi_write(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS); 276 277 /* gen2 tx power off */ 278 dwhdmi_phy_gen2_txpwron(sc, 0); 279 280 /* gen2 pddq */ 281 dwhdmi_phy_gen2_pddq(sc, 1); 282 283 /* PHY reset */ 284 dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT); 285 dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT); 286 287 dwhdmi_write(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT); 288 289 dwhdmi_phy_test_clear(sc, 1); 290 dwhdmi_write(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2); 291 dwhdmi_phy_test_clear(sc, 0); 292 293 /* 294 * Following initialization are for 8bit per color case 295 */ 296 297 /* 298 * PLL/MPLL config 299 */ 300 for (mpll_conf = &sc->sc_mpll_config[0]; mpll_conf->pixel_clock != 0; mpll_conf++) 301 if (mode->clock <= mpll_conf->pixel_clock) 302 break; 303 304 dwhdmi_phy_i2c_write(sc, mpll_conf->cpce, HDMI_PHY_I2C_CPCE_CTRL); 305 dwhdmi_phy_i2c_write(sc, mpll_conf->gmp, HDMI_PHY_I2C_GMPCTRL); 306 dwhdmi_phy_i2c_write(sc, mpll_conf->curr, HDMI_PHY_I2C_CURRCTRL); 307 308 for (phy_conf = &sc->sc_phy_config[0]; phy_conf->pixel_clock != 0; phy_conf++) 309 if (mode->clock <= phy_conf->pixel_clock) 310 break; 311 312 dwhdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL); 313 dwhdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL); 314 315 dwhdmi_phy_i2c_write(sc, phy_conf->term, HDMI_PHY_I2C_TXTERM); 316 dwhdmi_phy_i2c_write(sc, phy_conf->sym, HDMI_PHY_I2C_CKSYMTXCTRL); 317 dwhdmi_phy_i2c_write(sc, phy_conf->vlev, HDMI_PHY_I2C_VLEVCTRL); 318 319 /* REMOVE CLK TERM */ 320 dwhdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL); 321 322 dwhdmi_phy_enable_power(sc, 1); 323 324 /* toggle TMDS enable */ 325 dwhdmi_phy_enable_tmds(sc, 0); 326 dwhdmi_phy_enable_tmds(sc, 1); 327 328 /* gen2 tx power on */ 329 dwhdmi_phy_gen2_txpwron(sc, 1); 330 dwhdmi_phy_gen2_pddq(sc, 0); 331 332 switch (sc->sc_phytype) { 333 case 0xb2: /* MHL PHY HEAC */ 334 case 0xc2: /* MHL PHY */ 335 case 0xf3: /* HDMI 2.0 TX PHY */ 336 dwhdmi_phy_enable_svsret(sc, 1); 337 break; 338 } 339 340 /*Wait for PHY PLL lock */ 341 msec = 4; 342 val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; 343 while (val == 0) { 344 delay(1000); 345 if (msec-- == 0) { 346 printf("%s: PHY PLL not locked\n", 347 sc->sc_dev.dv_xname); 348 return (-1); 349 } 350 val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; 351 } 352 353 return (0); 354 } 355 356 void 357 dwhdmi_phy_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode) 358 { 359 int i; 360 361 /* HDMI Phy spec says to do the phy initialization sequence twice */ 362 for (i = 0 ; i < 2 ; i++) { 363 dwhdmi_phy_sel_data_en_pol(sc, 1); 364 dwhdmi_phy_sel_interface_control(sc, 0); 365 dwhdmi_phy_enable_tmds(sc, 0); 366 dwhdmi_phy_enable_power(sc, 0); 367 368 /* Enable CSC */ 369 dwhdmi_phy_configure(sc, mode); 370 } 371 } 372 373 enum drm_connector_status 374 dwhdmi_phy_detect(struct dwhdmi_softc *sc, int force) 375 { 376 uint8_t val; 377 378 val = dwhdmi_read(sc, HDMI_PHY_STAT0); 379 380 return ((val & HDMI_PHY_STAT0_HPD) != 0) ? 381 connector_status_connected : 382 connector_status_disconnected; 383 } 384 385 void 386 dwhdmi_phy_enable(struct dwhdmi_softc *sc) 387 { 388 } 389 390 void 391 dwhdmi_phy_disable(struct dwhdmi_softc *sc) 392 { 393 } 394 395 void 396 dwhdmi_phy_mode_set(struct dwhdmi_softc *sc, 397 struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 398 { 399 dwhdmi_phy_init(sc, adjusted_mode); 400 } 401