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);
+}

Reply via email to