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