Module Name: src Committed By: jmcneill Date: Sat Nov 9 23:27:51 UTC 2019
Modified Files: src/sys/conf: files src/sys/dev/ic: dw_hdmi.c dw_hdmi.h Added Files: src/sys/dev/ic: dw_hdmi_phy.c Log Message: Add support for internal DesignWare HDMI PHYs To generate a diff of this commit: cvs rdiff -u -r1.1241 -r1.1242 src/sys/conf/files cvs rdiff -u -r1.1 -r1.2 src/sys/dev/ic/dw_hdmi.c src/sys/dev/ic/dw_hdmi.h cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/dw_hdmi_phy.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/conf/files diff -u src/sys/conf/files:1.1241 src/sys/conf/files:1.1242 --- src/sys/conf/files:1.1241 Tue Nov 5 20:19:17 2019 +++ src/sys/conf/files Sat Nov 9 23:27:50 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1241 2019/11/05 20:19:17 maxv Exp $ +# $NetBSD: files,v 1.1242 2019/11/09 23:27:50 jmcneill Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20171118 @@ -1497,6 +1497,7 @@ attach ipmi at ipmibus # Designware HDMI TX device dwhdmi: edid, videomode, drmkms, drmkms_i2c file dev/ic/dw_hdmi.c dwhdmi +file dev/ic/dw_hdmi_phy.c dwhdmi # # File systems Index: src/sys/dev/ic/dw_hdmi.c diff -u src/sys/dev/ic/dw_hdmi.c:1.1 src/sys/dev/ic/dw_hdmi.c:1.2 --- src/sys/dev/ic/dw_hdmi.c:1.1 Wed Jan 30 01:19:49 2019 +++ src/sys/dev/ic/dw_hdmi.c Sat Nov 9 23:27:50 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */ +/* $NetBSD: dw_hdmi.c,v 1.2 2019/11/09 23:27:50 jmcneill Exp $ */ /*- * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.2 2019/11/09 23:27:50 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -50,6 +50,10 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> +#define HDMI_DESIGN_ID 0x0000 +#define HDMI_REVISION_ID 0x0001 +#define HDMI_CONFIG2_ID 0x0006 + #define HDMI_IH_I2CM_STAT0 0x0105 #define HDMI_IH_I2CM_STAT0_DONE __BIT(1) #define HDMI_IH_I2CM_STAT0_ERROR __BIT(0) @@ -128,6 +132,23 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #define HDMI_FC_CH2PREAM 0x1016 #define HDMI_FC_CH2PREAM_DEFAULT 0x21 +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_CONF0_PDZ __BIT(7) +#define HDMI_PHY_CONF0_ENTMDS __BIT(6) +#define HDMI_PHY_CONF0_SVSRET __BIT(5) +#define HDMI_PHY_CONF0_PDDQ __BIT(4) +#define HDMI_PHY_CONF0_TXPWRON __BIT(3) +#define HDMI_PHY_CONF0_ENHPDRXSENSE __BIT(2) +#define HDMI_PHY_CONF0_SELDATAENPOL __BIT(1) +#define HDMI_PHY_CONF0_SELDIPIF __BIT(0) +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_STAT0_RX_SENSE_3 __BIT(7) +#define HDMI_PHY_STAT0_RX_SENSE_2 __BIT(6) +#define HDMI_PHY_STAT0_RX_SENSE_1 __BIT(5) +#define HDMI_PHY_STAT0_RX_SENSE_0 __BIT(4) +#define HDMI_PHY_STAT0_HPD __BIT(1) +#define HDMI_PHY_STAT0_TX_PHY_LOCK __BIT(0) + #define HDMI_MC_CLKDIS 0x4001 #define HDMI_MC_CLKDIS_HDCPCLK_DISABLE __BIT(6) #define HDMI_MC_CLKDIS_CECCLK_DISABLE __BIT(5) @@ -143,6 +164,8 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #define HDMI_MC_SWRSTZREQ_PIXELSWRST_REQ __BIT(0) #define HDMI_MC_FLOWCTRL 0x4004 #define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_PHYRSTZ_ASSERT __BIT(0) +#define HDMI_MC_PHYRSTZ_DEASSERT 0 #define HDMI_MC_LOCKONCLOCK 0x4006 #define HDMI_MC_HEACPHY_RST 0x4007 @@ -412,7 +435,7 @@ dwhdmi_mc_init(struct dwhdmi_softc *sc) { struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector; uint8_t val; - u_int n; + u_int n, iter; /* Bypass colour space converter */ dwhdmi_write(sc, HDMI_MC_FLOWCTRL, 0); @@ -430,8 +453,10 @@ dwhdmi_mc_init(struct dwhdmi_softc *sc) val = 0xff & ~HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ; dwhdmi_write(sc, HDMI_MC_SWRSTZREQ, val); + iter = sc->sc_version == 0x130a ? 4 : 1; + val = dwhdmi_read(sc, HDMI_FC_INVIDCONF); - for (n = 0; n < 4; n++) + for (n = 0; n < iter; n++) dwhdmi_write(sc, HDMI_FC_INVIDCONF, val); } @@ -479,7 +504,7 @@ dwhdmi_connector_get_modes(struct drm_co memset(edid, 0, sizeof(edid)); for (block = 0; block < 4; block++) { - error = ddc_read_edid_block(&sc->sc_ic, + error = ddc_read_edid_block(sc->sc_ic, &edid[block * EDID_LENGTH], EDID_LENGTH, block); if (error != 0) break; @@ -624,7 +649,7 @@ static const struct drm_bridge_funcs dwh int dwhdmi_attach(struct dwhdmi_softc *sc) { - struct i2c_controller *ic = &sc->sc_ic; + uint8_t val; if (sc->sc_reg_width != 1 && sc->sc_reg_width != 4) { aprint_error_dev(sc->sc_dev, "unsupported register width %d\n", sc->sc_reg_width); @@ -633,10 +658,37 @@ dwhdmi_attach(struct dwhdmi_softc *sc) mutex_init(&sc->sc_ic_lock, MUTEX_DEFAULT, IPL_NONE); - ic->ic_cookie = sc; - ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus; - ic->ic_release_bus = dwhdmi_ddc_release_bus; - ic->ic_exec = dwhdmi_ddc_exec; + sc->sc_version = dwhdmi_read(sc, HDMI_DESIGN_ID); + sc->sc_version <<= 8; + sc->sc_version |= dwhdmi_read(sc, HDMI_REVISION_ID); + + sc->sc_phytype = dwhdmi_read(sc, HDMI_CONFIG2_ID); + + aprint_normal_dev(sc->sc_dev, "version %x.%03x, phytype 0x%02x\n", + sc->sc_version >> 12, sc->sc_version & 0xfff, + sc->sc_phytype); + + /* + * If a DDC i2c bus tag is provided by the caller, use it. Otherwise, + * use the I2C master built-in to DWC HDMI. + */ + if (sc->sc_ic == NULL) { + struct i2c_controller *ic = &sc->sc_ic_builtin; + ic->ic_cookie = sc; + ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus; + ic->ic_release_bus = dwhdmi_ddc_release_bus; + ic->ic_exec = dwhdmi_ddc_exec; + sc->sc_ic = ic; + } + + /* + * Enable HPD on internal PHY + */ + if ((sc->sc_flags & DWHDMI_USE_INTERNAL_PHY) != 0) { + val = dwhdmi_read(sc, HDMI_PHY_CONF0); + val |= HDMI_PHY_CONF0_ENHPDRXSENSE; + dwhdmi_write(sc, HDMI_PHY_CONF0, val); + } return 0; } Index: src/sys/dev/ic/dw_hdmi.h diff -u src/sys/dev/ic/dw_hdmi.h:1.1 src/sys/dev/ic/dw_hdmi.h:1.2 --- src/sys/dev/ic/dw_hdmi.h:1.1 Wed Jan 30 01:19:49 2019 +++ src/sys/dev/ic/dw_hdmi.h Sat Nov 9 23:27:50 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dw_hdmi.h,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */ +/* $NetBSD: dw_hdmi.h,v 1.2 2019/11/09 23:27:50 jmcneill Exp $ */ /*- * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> @@ -44,20 +44,43 @@ struct dwhdmi_connector { bool monitor_audio; }; +struct dwhdmi_phy_config { + u_int pixel_clock; + uint32_t sym; + uint32_t term; + uint32_t vlev; +}; + +struct dwhdmi_mpll_config { + u_int pixel_clock; + uint32_t cpce; + uint32_t gmp; + uint32_t curr; +}; + struct dwhdmi_softc { device_t sc_dev; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; u_int sc_reg_width; + u_int sc_flags; +#define DWHDMI_USE_INTERNAL_PHY __BIT(0) - struct i2c_controller sc_ic; + u_int sc_phytype; + u_int sc_version; + + i2c_tag_t sc_ic; kmutex_t sc_ic_lock; + struct i2c_controller sc_ic_builtin; struct dwhdmi_connector sc_connector; struct drm_bridge sc_bridge; struct drm_display_mode sc_curmode; + const struct dwhdmi_mpll_config *sc_mpll_config; + const struct dwhdmi_phy_config *sc_phy_config; + enum drm_connector_status (*sc_detect)(struct dwhdmi_softc *, bool); void (*sc_enable)(struct dwhdmi_softc *); void (*sc_disable)(struct dwhdmi_softc *); @@ -74,4 +97,11 @@ int dwhdmi_bind(struct dwhdmi_softc *, uint8_t dwhdmi_read(struct dwhdmi_softc *, bus_size_t); void dwhdmi_write(struct dwhdmi_softc *, bus_size_t, uint8_t); +enum drm_connector_status dwhdmi_phy_detect(struct dwhdmi_softc *, bool); +void dwhdmi_phy_enable(struct dwhdmi_softc *); +void dwhdmi_phy_disable(struct dwhdmi_softc *); +void dwhdmi_phy_mode_set(struct dwhdmi_softc *, + struct drm_display_mode *, + struct drm_display_mode *); + #endif /* !_DEV_IC_DWHDMI_H */ Added files: Index: src/sys/dev/ic/dw_hdmi_phy.c diff -u /dev/null src/sys/dev/ic/dw_hdmi_phy.c:1.1 --- /dev/null Sat Nov 9 23:27:51 2019 +++ src/sys/dev/ic/dw_hdmi_phy.c Sat Nov 9 23:27:50 2019 @@ -0,0 +1,401 @@ +/* $NetBSD: dw_hdmi_phy.c,v 1.1 2019/11/09 23:27:50 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko <go...@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: dw_hdmi_phy.c,v 1.1 2019/11/09 23:27:50 jmcneill Exp $"); + +#include <sys/param.h> + +#include <drm/drmP.h> + +#include <dev/ic/dw_hdmi.h> + +#define HDMI_IH_PHY_STAT0 0x0104 +#define HDMI_IH_PHY_STAT0_HPD (1 << 0) +#define HDMI_IH_I2CMPHY_STAT0 0x0108 +#define HDMI_IH_I2CMPHY_STAT0_DONE (1 << 1) +#define HDMI_IH_I2CMPHY_STAT0_ERROR (1 << 0) + +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_CONF0_PDZ_MASK 0x80 +#define HDMI_PHY_CONF0_PDZ_OFFSET 7 +#define HDMI_PHY_CONF0_ENTMDS_MASK 0x40 +#define HDMI_PHY_CONF0_ENTMDS_OFFSET 6 +#define HDMI_PHY_CONF0_SVSRET_MASK 0x20 +#define HDMI_PHY_CONF0_SVSRET_OFFSET 5 +#define HDMI_PHY_CONF0_GEN2_PDDQ_MASK 0x10 +#define HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET 4 +#define HDMI_PHY_CONF0_GEN2_TXPWRON_MASK 0x8 +#define HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET 3 +#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK 0x4 +#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET 2 +#define HDMI_PHY_CONF0_SELDATAENPOL_MASK 0x2 +#define HDMI_PHY_CONF0_SELDATAENPOL_OFFSET 1 +#define HDMI_PHY_CONF0_SELDIPIF_MASK 0x1 +#define HDMI_PHY_CONF0_SELDIPIF_OFFSET 0 +#define HDMI_PHY_TST0 0x3001 +#define HDMI_PHY_TST0_TSTCLR_MASK 0x20 +#define HDMI_PHY_TST0_TSTCLR_OFFSET 5 +#define HDMI_PHY_TST0_TSTEN_MASK 0x10 +#define HDMI_PHY_TST0_TSTEN_OFFSET 4 +#define HDMI_PHY_TST0_TSTCLK_MASK 0x1 +#define HDMI_PHY_TST0_TSTCLK_OFFSET 0 +#define HDMI_PHY_TST1 0x3002 +#define HDMI_PHY_TST2 0x3003 +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_STAT0_RX_SENSE3 0x80 +#define HDMI_PHY_STAT0_RX_SENSE2 0x40 +#define HDMI_PHY_STAT0_RX_SENSE1 0x20 +#define HDMI_PHY_STAT0_RX_SENSE0 0x10 +#define HDMI_PHY_STAT0_RX_SENSE 0xf0 +#define HDMI_PHY_STAT0_HPD 0x02 +#define HDMI_PHY_TX_PHY_LOCK 0x01 +#define HDMI_PHY_INT0 0x3005 +#define HDMI_PHY_MASK0 0x3006 +#define HDMI_PHY_POL0 0x3007 +#define HDMI_PHY_POL0_HPD 0x02 + +/* HDMI Master PHY Registers */ +#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 +#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69 +#define HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY 0x49 +#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 +#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 +#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 +#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 +#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 +#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 +#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10 +#define HDMI_PHY_I2CM_OPERATION_ADDR_READ 0x1 +#define HDMI_PHY_I2CM_INT_ADDR 0x3027 +#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 +#define HDMI_PHY_I2CM_DIV_ADDR 0x3029 +#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a +#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b +#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c +#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d +#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e +#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f +#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 + +#define HDMI_MC_FLOWCTRL 0x4004 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK 0x1 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH 0x1 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS 0x0 +#define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_PHYRSTZ_ASSERT 0x0 +#define HDMI_MC_PHYRSTZ_DEASSERT 0x1 +#define HDMI_MC_HEACPHY_RST 0x4007 +#define HDMI_MC_HEACPHY_RST_ASSERT 0x1 +#define HDMI_MC_HEACPHY_RST_DEASSERT 0x0 + +/* HDMI PHY register with access through I2C */ +#define HDMI_PHY_I2C_CKCALCTRL 0x5 +#define CKCALCTRL_OVERRIDE (1 << 15) +#define HDMI_PHY_I2C_CPCE_CTRL 0x6 +#define CPCE_CTRL_45_25 ((3 << 7) | (3 << 5)) +#define CPCE_CTRL_92_50 ((2 << 7) | (2 << 5)) +#define CPCE_CTRL_185 ((1 << 7) | (1 << 5)) +#define CPCE_CTRL_370 ((0 << 7) | (0 << 5)) +#define HDMI_PHY_I2C_CKSYMTXCTRL 0x9 +#define CKSYMTXCTRL_OVERRIDE (1 << 15) +#define CKSYMTXCTRL_TX_SYMON (1 << 3) +#define CKSYMTXCTRL_TX_TRAON (1 << 2) +#define CKSYMTXCTRL_TX_TRBON (1 << 1) +#define CKSYMTXCTRL_TX_CK_SYMON (1 << 0) +#define HDMI_PHY_I2C_VLEVCTRL 0x0E +#define HDMI_PHY_I2C_CURRCTRL 0x10 +#define HDMI_PHY_I2C_PLLPHBYCTRL 0x13 +#define VLEVCTRL_TX_LVL(x) ((x) << 5) +#define VLEVCTRL_CK_LVL(x) (x) +#define HDMI_PHY_I2C_GMPCTRL 0x15 +#define GMPCTRL_45_25 0x00 +#define GMPCTRL_92_50 0x05 +#define GMPCTRL_185 0x0a +#define GMPCTRL_370 0x0f +#define HDMI_PHY_I2C_MSM_CTRL 0x17 +#define MSM_CTRL_FB_CLK (0x3 << 1) +#define HDMI_PHY_I2C_TXTERM 0x19 +#define TXTERM_133 0x5 + +static void +dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc *sc, int msec) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & + (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + while (val == 0) { + delay(1000); + msec -= 10; + if (msec <= 0) + return; + val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & + (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + } +} + +static void +dwhdmi_phy_i2c_write(struct dwhdmi_softc *sc, unsigned short data, + unsigned char addr) +{ + + /* clear DONE and ERROR flags */ + dwhdmi_write(sc, HDMI_IH_I2CMPHY_STAT0, + HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + dwhdmi_write(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr); + dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff)); + dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff)); + dwhdmi_write(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE); + dwhdmi_phy_wait_i2c_done(sc, 1000); +} + +static void +dwhdmi_phy_enable_power(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_PDZ_MASK; + reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_enable_tmds(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK; + reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_gen2_pddq(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; + reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; + reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK; + reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_sel_interface_control(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK; + reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_enable_svsret(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SVSRET_MASK; + reg |= (enable << HDMI_PHY_CONF0_SVSRET_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static inline void +dwhdmi_phy_test_clear(struct dwhdmi_softc *sc, unsigned char bit) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_PHY_TST0); + val &= ~HDMI_PHY_TST0_TSTCLR_MASK; + val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & + HDMI_PHY_TST0_TSTCLR_MASK; + dwhdmi_write(sc, HDMI_PHY_TST0, val); +} + +static int +dwhdmi_phy_configure(struct dwhdmi_softc *sc, struct drm_display_mode *mode) +{ + const struct dwhdmi_mpll_config *mpll_conf; + const struct dwhdmi_phy_config *phy_conf; + uint8_t val; + uint8_t msec; + + dwhdmi_write(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS); + + /* gen2 tx power off */ + dwhdmi_phy_gen2_txpwron(sc, 0); + + /* gen2 pddq */ + dwhdmi_phy_gen2_pddq(sc, 1); + + /* PHY reset */ + dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT); + dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT); + + dwhdmi_write(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT); + + dwhdmi_phy_test_clear(sc, 1); + dwhdmi_write(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2); + dwhdmi_phy_test_clear(sc, 0); + + /* + * Following initialization are for 8bit per color case + */ + + /* + * PLL/MPLL config + */ + for (mpll_conf = &sc->sc_mpll_config[0]; mpll_conf->pixel_clock != 0; mpll_conf++) + if (mpll_conf->pixel_clock <= mode->clock) + break; + + dwhdmi_phy_i2c_write(sc, mpll_conf->cpce, HDMI_PHY_I2C_CPCE_CTRL); + dwhdmi_phy_i2c_write(sc, mpll_conf->gmp, HDMI_PHY_I2C_GMPCTRL); + dwhdmi_phy_i2c_write(sc, mpll_conf->curr, HDMI_PHY_I2C_CURRCTRL); + + for (phy_conf = &sc->sc_phy_config[0]; phy_conf->pixel_clock != 0; phy_conf++) + if (phy_conf->pixel_clock <= mode->clock) + break; + + dwhdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL); + dwhdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL); + + dwhdmi_phy_i2c_write(sc, phy_conf->term, HDMI_PHY_I2C_TXTERM); + dwhdmi_phy_i2c_write(sc, phy_conf->sym, HDMI_PHY_I2C_CKSYMTXCTRL); + dwhdmi_phy_i2c_write(sc, phy_conf->vlev, HDMI_PHY_I2C_VLEVCTRL); + + /* REMOVE CLK TERM */ + dwhdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL); + + dwhdmi_phy_enable_power(sc, 1); + + /* toggle TMDS enable */ + dwhdmi_phy_enable_tmds(sc, 0); + dwhdmi_phy_enable_tmds(sc, 1); + + /* gen2 tx power on */ + dwhdmi_phy_gen2_txpwron(sc, 1); + dwhdmi_phy_gen2_pddq(sc, 0); + + switch (sc->sc_phytype) { + case 0xb2: /* MHL PHY HEAC */ + case 0xc2: /* MHL PHY */ + case 0xf3: /* HDMI 2.0 TX PHY */ + dwhdmi_phy_enable_svsret(sc, 1); + break; + } + + /*Wait for PHY PLL lock */ + msec = 4; + val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + while (val == 0) { + delay(1000); + if (msec-- == 0) { + device_printf(sc->sc_dev, "PHY PLL not locked\n"); + return (-1); + } + val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + } + + return (0); +} + +static void +dwhdmi_phy_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode) +{ + int i; + + /* HDMI Phy spec says to do the phy initialization sequence twice */ + for (i = 0 ; i < 2 ; i++) { + dwhdmi_phy_sel_data_en_pol(sc, 1); + dwhdmi_phy_sel_interface_control(sc, 0); + dwhdmi_phy_enable_tmds(sc, 0); + dwhdmi_phy_enable_power(sc, 0); + + /* Enable CSC */ + dwhdmi_phy_configure(sc, mode); + } +} + +enum drm_connector_status +dwhdmi_phy_detect(struct dwhdmi_softc *sc, bool force) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_PHY_STAT0); + + return ((val & HDMI_PHY_STAT0_HPD) != 0) ? + connector_status_connected : + connector_status_disconnected; +} + +void +dwhdmi_phy_enable(struct dwhdmi_softc *sc) +{ +} + +void +dwhdmi_phy_disable(struct dwhdmi_softc *sc) +{ +} + +void +dwhdmi_phy_mode_set(struct dwhdmi_softc *sc, + struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + dwhdmi_phy_init(sc, adjusted_mode); +}