1 /* $NetBSD: sunxi_hdmiphy.c,v 1.4 2019/11/23 18:54:26 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca> 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 31 __KERNEL_RCSID(0, "$NetBSD: sunxi_hdmiphy.c,v 1.4 2019/11/23 18:54:26 jmcneill Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/device.h> 36 #include <sys/intr.h> 37 #include <sys/systm.h> 38 39 #include <dev/fdt/fdtvar.h> 40 41 #include <arm/sunxi/sunxi_hdmiphy.h> 42 43 #define DBG_CTRL 0x000 44 #define DBG_CTRL_POL __BITS(15,8) 45 #define DBG_CTRL_POL_NVSYNC 1 46 #define DBG_CTRL_POL_NHSYNC 2 47 48 #define READ_EN 0x010 49 #define READ_EN_MAGIC 0x54524545 /* "TREE" */ 50 51 #define UNSCRAMBLE 0x014 52 #define UNSCRAMBLE_MAGIC 0x42494E47 /* "BING" */ 53 54 #define ANA_CFG1 0x020 55 #define ANA_CFG1_ENRCAL __BIT(19) 56 #define ANA_CFG1_ENCALOG __BIT(18) 57 #define ANA_CFG1_TMDSCLK_EN __BIT(16) 58 #define ANA_CFG1_TXEN __BITS(15,12) 59 #define ANA_CFG1_BIASEN __BITS(11,8) 60 #define ANA_CFG1_ENP2S __BITS(7,4) 61 #define ANA_CFG1_CKEN __BIT(3) 62 #define ANA_CFG1_LDOEN __BIT(2) 63 #define ANA_CFG1_ENVBS __BIT(1) 64 #define ANA_CFG1_ENBI __BIT(0) 65 66 #define ANA_CFG2 0x024 67 #define ANA_CFG2_REG_RESDI __BITS(5,0) 68 69 #define ANA_CFG3 0x028 70 #define ANA_CFG3_REG_SDAEN __BIT(2) 71 #define ANA_CFG3_REG_SCLEN __BIT(0) 72 73 #define PLL_CFG1 0x02c 74 #define PLL_CFG1_REG_OD1 __BIT(31) 75 #define PLL_CFG1_REG_OD0 __BIT(30) 76 #define PLL_CFG1_CKIN_SEL __BIT(26) 77 #define PLL_CFG1_PLLEN __BIT(25) 78 #define PLL_CFG1_B_IN __BITS(5,0) 79 80 #define PLL_CFG2 0x030 81 #define PLL_CFG2_PREDIV __BITS(3,0) 82 83 #define PLL_CFG3 0x034 84 85 #define ANA_STS 0x038 86 #define ANA_STS_HPDO __BIT(19) 87 #define ANA_STS_B_OUT __BITS(16,11) 88 #define ANA_STS_RCALEND2D __BIT(7) 89 #define ANA_STS_RESDO2D __BITS(5,0) 90 91 #define CEC 0x03c 92 #define CEC_CONTROL_SEL __BIT(7) 93 #define CEC_INPUT_DATA __BIT(1) 94 #define CEC_OUTPUT_DATA __BIT(0) 95 96 #define CONTROLLER_VER 0xff8 97 98 #define PHY_VER 0xffc 99 100 struct sunxi_hdmiphy_softc; 101 102 static int sunxi_hdmiphy_match(device_t, cfdata_t, void *); 103 static void sunxi_hdmiphy_attach(device_t, device_t, void *); 104 105 static void sun8i_h3_hdmiphy_init(struct sunxi_hdmiphy_softc *); 106 static int sun8i_h3_hdmiphy_config(struct sunxi_hdmiphy_softc *, u_int); 107 108 struct sunxi_hdmiphy_data { 109 void (*init)(struct sunxi_hdmiphy_softc *); 110 int (*config)(struct sunxi_hdmiphy_softc *, u_int); 111 }; 112 113 static const struct sunxi_hdmiphy_data sun8i_h3_hdmiphy_data = { 114 .init = sun8i_h3_hdmiphy_init, 115 .config = sun8i_h3_hdmiphy_config, 116 }; 117 118 static const struct of_compat_data compat_data[] = { 119 { "allwinner,sun8i-h3-hdmi-phy", (uintptr_t)&sun8i_h3_hdmiphy_data }, 120 { "allwinner,sun50i-a64-hdmi-phy", (uintptr_t)&sun8i_h3_hdmiphy_data }, 121 { NULL } 122 }; 123 124 struct sunxi_hdmiphy_softc { 125 device_t sc_dev; 126 bus_space_tag_t sc_bst; 127 bus_space_handle_t sc_bsh; 128 129 const struct sunxi_hdmiphy_data *sc_data; 130 131 struct fdtbus_reset *sc_rst; 132 struct clk *sc_clk_bus; 133 struct clk *sc_clk_mod; 134 struct clk *sc_clk_pll0; 135 136 u_int sc_rcalib; 137 }; 138 139 #define PHY_READ(sc, reg) \ 140 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 141 #define PHY_WRITE(sc, reg, val) \ 142 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 143 #define PHY_SET_CLEAR(sc, reg, set, clr) \ 144 do { \ 145 uint32_t _tval = PHY_READ((sc), (reg)); \ 146 _tval &= ~(clr); \ 147 _tval |= (set); \ 148 PHY_WRITE((sc), (reg), _tval); \ 149 } while (0) 150 #define PHY_SET(sc, reg, set) \ 151 PHY_SET_CLEAR(sc, reg, set, 0) 152 #define PHY_CLEAR(sc, reg, clr) \ 153 PHY_SET_CLEAR(sc, reg, 0, clr) 154 155 CFATTACH_DECL_NEW(sunxi_hdmiphy, sizeof(struct sunxi_hdmiphy_softc), 156 sunxi_hdmiphy_match, sunxi_hdmiphy_attach, NULL, NULL); 157 158 static void * 159 sunxi_hdmiphy_acquire(device_t dev, const void *data, size_t len) 160 { 161 struct sunxi_hdmiphy_softc * const sc = device_private(dev); 162 163 if (len != 0) 164 return NULL; 165 166 return sc; 167 } 168 169 static void 170 sunxi_hdmiphy_release(device_t dev, void *priv) 171 { 172 } 173 174 static int 175 sunxi_hdmiphy_enable(device_t dev, void *priv, bool enable) 176 { 177 return 0; 178 } 179 180 static const struct fdtbus_phy_controller_func sunxi_hdmiphy_funcs = { 181 .acquire = sunxi_hdmiphy_acquire, 182 .release = sunxi_hdmiphy_release, 183 .enable = sunxi_hdmiphy_enable, 184 }; 185 186 #ifdef SUNXI_HDMIPHY_DEBUG 187 static void 188 sunxi_hdmiphy_dump(struct sunxi_hdmiphy_softc *sc) 189 { 190 device_printf(sc->sc_dev, "ANA_CFG1: %#x\tANA_CFG2: %#x\tANA_CFG3: %#x\n", 191 PHY_READ(sc, ANA_CFG1), PHY_READ(sc, ANA_CFG2), PHY_READ(sc, ANA_CFG3)); 192 device_printf(sc->sc_dev, "PLL_CFG1: %#x\tPLL_CFG2: %#x\tPLL_CFG3: %#x\n", 193 PHY_READ(sc, PLL_CFG1), PHY_READ(sc, PLL_CFG2), PHY_READ(sc, PLL_CFG3)); 194 device_printf(sc->sc_dev, "DBG_CTRL: %#x\tANA_STS: %#x\n", 195 PHY_READ(sc, DBG_CTRL), PHY_READ(sc, ANA_STS)); 196 } 197 #endif 198 199 static void 200 sun8i_h3_hdmiphy_init(struct sunxi_hdmiphy_softc *sc) 201 { 202 uint32_t val; 203 int retry; 204 205 PHY_WRITE(sc, ANA_CFG1, 0); 206 207 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENBI); 208 delay(5); 209 210 /* Enable TMDS clock */ 211 PHY_SET(sc, ANA_CFG1, ANA_CFG1_TMDSCLK_EN); 212 213 /* Enable common voltage reference bias module */ 214 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENVBS); 215 delay(20); 216 217 /* Enable internal LDO */ 218 PHY_SET(sc, ANA_CFG1, ANA_CFG1_LDOEN); 219 delay(5); 220 221 /* Enable common clock module */ 222 PHY_SET(sc, ANA_CFG1, ANA_CFG1_CKEN); 223 delay(100); 224 225 /* Enable resistance calibration analog and digital modules */ 226 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENRCAL); 227 delay(200); 228 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENCALOG); 229 230 /* P2S module enable for TMDS data lane */ 231 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0x7, ANA_CFG1_ENP2S), ANA_CFG1_ENP2S); 232 233 /* Wait for resistance calibration to finish */ 234 for (retry = 2000; retry > 0; retry--) { 235 if ((PHY_READ(sc, ANA_STS) & ANA_STS_RCALEND2D) != 0) 236 break; 237 delay(1); 238 } 239 if (retry == 0) 240 aprint_error_dev(sc->sc_dev, "HDMI PHY resistance calibration timed out\n"); 241 242 /* Enable current and voltage module */ 243 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0xf, ANA_CFG1_BIASEN), ANA_CFG1_BIASEN); 244 245 /* P2S module enable for TMDS clock lane */ 246 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0xf, ANA_CFG1_ENP2S), ANA_CFG1_ENP2S); 247 248 /* Enable DDC */ 249 PHY_SET(sc, ANA_CFG3, ANA_CFG3_REG_SDAEN | ANA_CFG3_REG_SCLEN); 250 251 /* Set parent clock to videopll0 */ 252 PHY_CLEAR(sc, PLL_CFG1, PLL_CFG1_CKIN_SEL); 253 254 /* Clear software control of CEC pins */ 255 PHY_CLEAR(sc, CEC, CEC_CONTROL_SEL); 256 257 /* Read calibration value for source termination resistors */ 258 val = PHY_READ(sc, ANA_STS); 259 sc->sc_rcalib = __SHIFTOUT(val, ANA_STS_RESDO2D); 260 } 261 262 /* 263 * The following table is based on data from the "HDMI TX PHY S40 Specification". 264 */ 265 static const struct sun8i_h3_hdmiphy_init { 266 /* PLL Recommended Configuration */ 267 uint32_t pll_cfg1; 268 uint32_t pll_cfg2; 269 uint32_t pll_cfg3; 270 /* TMDS Characteristics Recommended Configuration */ 271 uint32_t ana_cfg1; 272 uint32_t ana_cfg2; 273 uint32_t ana_cfg3; 274 bool ana_cfg2_rcal_200; 275 u_int b_offset; 276 } sun8i_h3_hdmiphy_inittab[] = { 277 /* 27 MHz */ 278 [0] = { 279 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x8008430a, .pll_cfg3 = 0x1, 280 .ana_cfg1 = 0x11ffff7f, .ana_cfg2 = 0x80623000, .ana_cfg3 = 0x0f80c285, 281 .ana_cfg2_rcal_200 = true, 282 }, 283 /* 74.25 MHz */ 284 [1] = { 285 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x80084343, .pll_cfg3 = 0x1, 286 .ana_cfg1 = 0x11ffff7f, .ana_cfg2 = 0x80623000, .ana_cfg3 = 0x0f814385, 287 .ana_cfg2_rcal_200 = true, 288 }, 289 /* 148.5 MHz */ 290 [2] = { 291 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x80084381, .pll_cfg3 = 0x1, 292 .ana_cfg1 = 0x01ffff7f, .ana_cfg2 = 0x8063a800, .ana_cfg3 = 0x0f81c485, 293 }, 294 /* 297 MHz */ 295 [3] = { 296 .pll_cfg1 = 0x35dc5fc0, .pll_cfg2 = 0x800863c0, .pll_cfg3 = 0x1, 297 .ana_cfg1 = 0x01ffff7f, .ana_cfg2 = 0x8063b000, .ana_cfg3 = 0x0f8246b5, 298 .b_offset = 2, 299 }, 300 }; 301 302 static int 303 sun8i_h3_hdmiphy_config(struct sunxi_hdmiphy_softc *sc, u_int rate) 304 { 305 const struct sun8i_h3_hdmiphy_init *inittab; 306 u_int init_index, b_out, prediv; 307 uint32_t val, rcalib; 308 309 if (rate == 0) { 310 /* Disable the PHY */ 311 PHY_WRITE(sc, ANA_CFG1, ANA_CFG1_LDOEN | ANA_CFG1_ENVBS | ANA_CFG1_ENBI); 312 PHY_WRITE(sc, PLL_CFG1, 0); 313 return 0; 314 } 315 316 init_index = 0; 317 if (rate > 27000000) 318 init_index++; 319 if (rate > 74250000) 320 init_index++; 321 if (rate > 148500000) 322 init_index++; 323 inittab = &sun8i_h3_hdmiphy_inittab[init_index]; 324 325 val = PHY_READ(sc, PLL_CFG2); 326 prediv = val & PLL_CFG2_PREDIV; 327 328 /* Config PLL */ 329 PHY_WRITE(sc, PLL_CFG1, inittab->pll_cfg1 & ~PLL_CFG1_CKIN_SEL); 330 PHY_WRITE(sc, PLL_CFG2, (inittab->pll_cfg2 & ~PLL_CFG2_PREDIV) | prediv); 331 delay(15000); 332 PHY_WRITE(sc, PLL_CFG3, inittab->pll_cfg3); 333 334 /* Enable PLL */ 335 PHY_SET(sc, PLL_CFG1, PLL_CFG1_PLLEN); 336 delay(100000); 337 338 /* Config PLL */ 339 val = PHY_READ(sc, ANA_STS); 340 b_out = __SHIFTOUT(val, ANA_STS_B_OUT); 341 b_out = MIN(b_out + inittab->b_offset, __SHIFTOUT_MASK(ANA_STS_B_OUT)); 342 343 PHY_SET(sc, PLL_CFG1, PLL_CFG1_REG_OD1 | PLL_CFG1_REG_OD0); 344 PHY_SET(sc, PLL_CFG1, __SHIFTIN(b_out, PLL_CFG1_B_IN)); 345 delay(100000); 346 347 /* Config TMDS characteristics */ 348 if (inittab->ana_cfg2_rcal_200) 349 rcalib = sc->sc_rcalib >> 2; 350 else 351 rcalib = 0; 352 PHY_WRITE(sc, ANA_CFG1, inittab->ana_cfg1); 353 PHY_WRITE(sc, ANA_CFG2, inittab->ana_cfg2 | rcalib); 354 PHY_WRITE(sc, ANA_CFG3, inittab->ana_cfg3); 355 356 #ifdef SUNXI_HDMIPHY_DEBUG 357 sunxi_hdmiphy_dump(sc); 358 #endif 359 360 return 0; 361 } 362 363 static int 364 sunxi_hdmiphy_set_rate(struct sunxi_hdmiphy_softc *sc, u_int new_rate) 365 { 366 u_int prediv, best_prediv, best_rate; 367 368 if (sc->sc_clk_pll0 == NULL) 369 return 0; 370 371 const u_int parent_rate = clk_get_rate(sc->sc_clk_pll0); 372 373 best_rate = 0; 374 375 for (prediv = 0; prediv <= __SHIFTOUT_MASK(PLL_CFG2_PREDIV); prediv++) { 376 const u_int tmp_rate = parent_rate / (prediv + 1); 377 const int diff = new_rate - tmp_rate; 378 if (diff >= 0 && tmp_rate > best_rate) { 379 best_rate = tmp_rate; 380 best_prediv = prediv; 381 } 382 } 383 384 if (best_rate == 0) 385 return ERANGE; 386 387 PHY_SET_CLEAR(sc, PLL_CFG2, __SHIFTIN(best_prediv, PLL_CFG2_PREDIV), PLL_CFG2_PREDIV); 388 389 return 0; 390 } 391 392 static int 393 sunxi_hdmiphy_match(device_t parent, cfdata_t cf, void *aux) 394 { 395 struct fdt_attach_args * const faa = aux; 396 397 return of_match_compat_data(faa->faa_phandle, compat_data); 398 } 399 400 static void 401 sunxi_hdmiphy_attach(device_t parent, device_t self, void *aux) 402 { 403 struct sunxi_hdmiphy_softc * const sc = device_private(self); 404 struct fdt_attach_args * const faa = aux; 405 const int phandle = faa->faa_phandle; 406 struct clk *clk_bus, *clk_mod, *clk_pll0; 407 struct fdtbus_reset *rst; 408 bus_addr_t addr; 409 bus_size_t size; 410 411 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 412 aprint_error(": couldn't get registers\n"); 413 return; 414 } 415 416 rst = fdtbus_reset_get(phandle, "phy"); 417 if (rst == NULL) { 418 aprint_error(": couldn't get reset\n"); 419 return; 420 } 421 clk_bus = fdtbus_clock_get(phandle, "bus"); 422 clk_mod = fdtbus_clock_get(phandle, "mod"); 423 clk_pll0 = fdtbus_clock_get(phandle, "pll-0"); 424 if (clk_bus == NULL || clk_mod == NULL || clk_pll0 == NULL) { 425 aprint_error(": couldn't get clocks\n"); 426 return; 427 } 428 429 sc->sc_dev = self; 430 sc->sc_bst = faa->faa_bst; 431 sc->sc_data = (void *)of_search_compatible(phandle, compat_data)->data; 432 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 433 aprint_error(": couldn't map registers\n"); 434 return; 435 } 436 sc->sc_rst = rst; 437 sc->sc_clk_bus = clk_bus; 438 sc->sc_clk_mod = clk_mod; 439 sc->sc_clk_pll0 = clk_pll0; 440 441 aprint_naive("\n"); 442 aprint_normal(": HDMI PHY\n"); 443 444 fdtbus_register_phy_controller(self, phandle, &sunxi_hdmiphy_funcs); 445 } 446 447 void 448 sunxi_hdmiphy_init(struct fdtbus_phy *phy) 449 { 450 device_t dev = fdtbus_phy_device(phy); 451 struct sunxi_hdmiphy_softc * const sc = device_private(dev); 452 453 clk_enable(sc->sc_clk_bus); 454 clk_enable(sc->sc_clk_mod); 455 clk_enable(sc->sc_clk_pll0); 456 457 fdtbus_reset_deassert(sc->sc_rst); 458 459 sc->sc_data->init(sc); 460 461 PHY_WRITE(sc, READ_EN, READ_EN_MAGIC); 462 PHY_WRITE(sc, UNSCRAMBLE, UNSCRAMBLE_MAGIC); 463 464 #ifdef SUNXI_HDMIPHY_DEBUG 465 sunxi_hdmiphy_dump(sc); 466 #endif 467 } 468 469 int 470 sunxi_hdmiphy_config(struct fdtbus_phy *phy, struct drm_display_mode *mode) 471 { 472 device_t dev = fdtbus_phy_device(phy); 473 struct sunxi_hdmiphy_softc * const sc = device_private(dev); 474 u_int pol; 475 int error; 476 477 pol = 0; 478 if ((mode->flags & DRM_MODE_FLAG_NHSYNC) != 0) 479 pol |= __SHIFTIN(DBG_CTRL_POL_NHSYNC, DBG_CTRL_POL); 480 if ((mode->flags & DRM_MODE_FLAG_NVSYNC) != 0) 481 pol |= __SHIFTIN(DBG_CTRL_POL_NVSYNC, DBG_CTRL_POL); 482 483 PHY_SET_CLEAR(sc, DBG_CTRL, pol, DBG_CTRL_POL); 484 485 error = sunxi_hdmiphy_set_rate(sc, mode->crtc_clock * 1000); 486 if (error != 0) { 487 aprint_error_dev(dev, "failed to set HDMI PHY clock: %d\n", error); 488 return error; 489 } 490 491 return sc->sc_data->config(sc, mode->crtc_clock * 1000); 492 } 493 494 bool 495 sunxi_hdmiphy_detect(struct fdtbus_phy *phy, bool force) 496 { 497 device_t dev = fdtbus_phy_device(phy); 498 struct sunxi_hdmiphy_softc * const sc = device_private(dev); 499 uint32_t val; 500 501 val = PHY_READ(sc, ANA_STS); 502 503 return ISSET(val, ANA_STS_HPDO); 504 } 505