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