xref: /freebsd-src/sys/dev/hdmi/dwc_hdmi.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
106785ff6SJared McNeill /*-
206785ff6SJared McNeill  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
306785ff6SJared McNeill  * All rights reserved.
406785ff6SJared McNeill  *
506785ff6SJared McNeill  * Redistribution and use in source and binary forms, with or without
606785ff6SJared McNeill  * modification, are permitted provided that the following conditions
706785ff6SJared McNeill  * are met:
806785ff6SJared McNeill  * 1. Redistributions of source code must retain the above copyright
906785ff6SJared McNeill  *    notice, this list of conditions and the following disclaimer.
1006785ff6SJared McNeill  * 2. Redistributions in binary form must reproduce the above copyright
1106785ff6SJared McNeill  *    notice, this list of conditions and the following disclaimer in the
1206785ff6SJared McNeill  *    documentation and/or other materials provided with the distribution.
1306785ff6SJared McNeill  *
1406785ff6SJared McNeill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1506785ff6SJared McNeill  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1606785ff6SJared McNeill  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1706785ff6SJared McNeill  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1806785ff6SJared McNeill  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1906785ff6SJared McNeill  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2006785ff6SJared McNeill  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2106785ff6SJared McNeill  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2206785ff6SJared McNeill  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2306785ff6SJared McNeill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2406785ff6SJared McNeill  * SUCH DAMAGE.
2506785ff6SJared McNeill  */
2606785ff6SJared McNeill 
2706785ff6SJared McNeill #include <sys/cdefs.h>
2806785ff6SJared McNeill /*
2906785ff6SJared McNeill  * HDMI core module
3006785ff6SJared McNeill  */
3106785ff6SJared McNeill 
3206785ff6SJared McNeill #include <sys/param.h>
3306785ff6SJared McNeill #include <sys/systm.h>
34e12be321SConrad Meyer #include <sys/eventhandler.h>
3506785ff6SJared McNeill #include <sys/kernel.h>
3606785ff6SJared McNeill #include <sys/module.h>
3706785ff6SJared McNeill #include <sys/malloc.h>
3806785ff6SJared McNeill #include <sys/bus.h>
3906785ff6SJared McNeill #include <sys/rman.h>
4006785ff6SJared McNeill 
4106785ff6SJared McNeill #include <machine/bus.h>
4206785ff6SJared McNeill 
4306785ff6SJared McNeill #include <dev/videomode/videomode.h>
4406785ff6SJared McNeill #include <dev/videomode/edidvar.h>
4506785ff6SJared McNeill 
4606785ff6SJared McNeill #include <dev/iicbus/iicbus.h>
4706785ff6SJared McNeill #include <dev/iicbus/iiconf.h>
4806785ff6SJared McNeill 
4906785ff6SJared McNeill #include <dev/hdmi/dwc_hdmi.h>
5006785ff6SJared McNeill #include <dev/hdmi/dwc_hdmireg.h>
5106785ff6SJared McNeill 
52*00e84f52SEmmanuel Vadot #include "crtc_if.h"
5306785ff6SJared McNeill 
5406785ff6SJared McNeill #define	I2C_DDC_ADDR	(0x50 << 1)
556443acaaSJared McNeill #define	I2C_DDC_SEGADDR	(0x30 << 1)
5606785ff6SJared McNeill #define	EDID_LENGTH	0x80
5706785ff6SJared McNeill 
586443acaaSJared McNeill #define	EXT_TAG			0x00
596443acaaSJared McNeill #define	CEA_TAG_ID		0x02
606443acaaSJared McNeill #define	CEA_DTD			0x03
616443acaaSJared McNeill #define	DTD_BASIC_AUDIO		(1 << 6)
626443acaaSJared McNeill #define	CEA_REV			0x02
636443acaaSJared McNeill #define	CEA_DATA_OFF		0x03
646443acaaSJared McNeill #define	CEA_DATA_START		4
656443acaaSJared McNeill #define	BLOCK_TAG(x)		(((x) >> 5) & 0x7)
666443acaaSJared McNeill #define	BLOCK_TAG_VSDB		3
676443acaaSJared McNeill #define	BLOCK_LEN(x)		((x) & 0x1f)
686443acaaSJared McNeill #define	HDMI_VSDB_MINLEN	5
696443acaaSJared McNeill #define	HDMI_OUI		"\x03\x0c\x00"
706443acaaSJared McNeill #define	HDMI_OUI_LEN		3
716443acaaSJared McNeill 
7206785ff6SJared McNeill static void
dwc_hdmi_phy_wait_i2c_done(struct dwc_hdmi_softc * sc,int msec)7306785ff6SJared McNeill dwc_hdmi_phy_wait_i2c_done(struct dwc_hdmi_softc *sc, int msec)
7406785ff6SJared McNeill {
7506785ff6SJared McNeill 	uint8_t val;
7606785ff6SJared McNeill 
7706785ff6SJared McNeill 	val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
7806785ff6SJared McNeill 	    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
7906785ff6SJared McNeill 	while (val == 0) {
8006785ff6SJared McNeill 		pause("HDMI_PHY", hz/100);
8106785ff6SJared McNeill 		msec -= 10;
8206785ff6SJared McNeill 		if (msec <= 0)
8306785ff6SJared McNeill 			return;
8406785ff6SJared McNeill 		val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
8506785ff6SJared McNeill 		    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
8606785ff6SJared McNeill 	}
8706785ff6SJared McNeill }
8806785ff6SJared McNeill 
8906785ff6SJared McNeill static void
dwc_hdmi_phy_i2c_write(struct dwc_hdmi_softc * sc,unsigned short data,unsigned char addr)9006785ff6SJared McNeill dwc_hdmi_phy_i2c_write(struct dwc_hdmi_softc *sc, unsigned short data,
9106785ff6SJared McNeill     unsigned char addr)
9206785ff6SJared McNeill {
9306785ff6SJared McNeill 
9406785ff6SJared McNeill 	/* clear DONE and ERROR flags */
9506785ff6SJared McNeill 	WR1(sc, HDMI_IH_I2CMPHY_STAT0,
9606785ff6SJared McNeill 	    HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
9706785ff6SJared McNeill 	WR1(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
9806785ff6SJared McNeill 	WR1(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff));
9906785ff6SJared McNeill 	WR1(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff));
10006785ff6SJared McNeill 	WR1(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
10106785ff6SJared McNeill 	dwc_hdmi_phy_wait_i2c_done(sc, 1000);
10206785ff6SJared McNeill }
10306785ff6SJared McNeill 
10406785ff6SJared McNeill static void
dwc_hdmi_disable_overflow_interrupts(struct dwc_hdmi_softc * sc)10506785ff6SJared McNeill dwc_hdmi_disable_overflow_interrupts(struct dwc_hdmi_softc *sc)
10606785ff6SJared McNeill {
10706785ff6SJared McNeill 	WR1(sc, HDMI_IH_MUTE_FC_STAT2, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK);
10806785ff6SJared McNeill 	WR1(sc, HDMI_FC_MASK2,
10906785ff6SJared McNeill 	    HDMI_FC_MASK2_LOW_PRI | HDMI_FC_MASK2_HIGH_PRI);
11006785ff6SJared McNeill }
11106785ff6SJared McNeill 
11206785ff6SJared McNeill static void
dwc_hdmi_av_composer(struct dwc_hdmi_softc * sc)11306785ff6SJared McNeill dwc_hdmi_av_composer(struct dwc_hdmi_softc *sc)
11406785ff6SJared McNeill {
11506785ff6SJared McNeill 	uint8_t inv_val;
11606785ff6SJared McNeill 	int is_dvi;
11706785ff6SJared McNeill 	int hblank, vblank, hsync_len, hfp, vfp;
11806785ff6SJared McNeill 
11906785ff6SJared McNeill 	/* Set up HDMI_FC_INVIDCONF */
12006785ff6SJared McNeill 	inv_val = ((sc->sc_mode.flags & VID_PVSYNC) ?
12106785ff6SJared McNeill 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
12206785ff6SJared McNeill 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
12306785ff6SJared McNeill 
12406785ff6SJared McNeill 	inv_val |= ((sc->sc_mode.flags & VID_PHSYNC) ?
12506785ff6SJared McNeill 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
12606785ff6SJared McNeill 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
12706785ff6SJared McNeill 
12806785ff6SJared McNeill 	inv_val |= HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH;
12906785ff6SJared McNeill 
13006785ff6SJared McNeill 	inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
13106785ff6SJared McNeill 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
13206785ff6SJared McNeill 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
13306785ff6SJared McNeill 
13406785ff6SJared McNeill 	inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
13506785ff6SJared McNeill 		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
13606785ff6SJared McNeill 		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
13706785ff6SJared McNeill 
13806785ff6SJared McNeill 	/* TODO: implement HDMI part */
1396443acaaSJared McNeill 	is_dvi = sc->sc_has_audio == 0;
14006785ff6SJared McNeill 	inv_val |= (is_dvi ?
14106785ff6SJared McNeill 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
14206785ff6SJared McNeill 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
14306785ff6SJared McNeill 
14406785ff6SJared McNeill 	WR1(sc, HDMI_FC_INVIDCONF, inv_val);
14506785ff6SJared McNeill 
14606785ff6SJared McNeill 	/* Set up horizontal active pixel region width */
14706785ff6SJared McNeill 	WR1(sc, HDMI_FC_INHACTV1, sc->sc_mode.hdisplay >> 8);
14806785ff6SJared McNeill 	WR1(sc, HDMI_FC_INHACTV0, sc->sc_mode.hdisplay);
14906785ff6SJared McNeill 
15006785ff6SJared McNeill 	/* Set up vertical blanking pixel region width */
15106785ff6SJared McNeill 	WR1(sc, HDMI_FC_INVACTV1, sc->sc_mode.vdisplay >> 8);
15206785ff6SJared McNeill 	WR1(sc, HDMI_FC_INVACTV0, sc->sc_mode.vdisplay);
15306785ff6SJared McNeill 
15406785ff6SJared McNeill 	/* Set up horizontal blanking pixel region width */
15506785ff6SJared McNeill 	hblank = sc->sc_mode.htotal - sc->sc_mode.hdisplay;
15606785ff6SJared McNeill 	WR1(sc, HDMI_FC_INHBLANK1, hblank >> 8);
15706785ff6SJared McNeill 	WR1(sc, HDMI_FC_INHBLANK0, hblank);
15806785ff6SJared McNeill 
15906785ff6SJared McNeill 	/* Set up vertical blanking pixel region width */
16006785ff6SJared McNeill 	vblank = sc->sc_mode.vtotal - sc->sc_mode.vdisplay;
16106785ff6SJared McNeill 	WR1(sc, HDMI_FC_INVBLANK, vblank);
16206785ff6SJared McNeill 
16306785ff6SJared McNeill 	/* Set up HSYNC active edge delay width (in pixel clks) */
16406785ff6SJared McNeill 	hfp = sc->sc_mode.hsync_start - sc->sc_mode.hdisplay;
16506785ff6SJared McNeill 	WR1(sc, HDMI_FC_HSYNCINDELAY1, hfp >> 8);
16606785ff6SJared McNeill 	WR1(sc, HDMI_FC_HSYNCINDELAY0, hfp);
16706785ff6SJared McNeill 
16806785ff6SJared McNeill 	/* Set up VSYNC active edge delay (in pixel clks) */
16906785ff6SJared McNeill 	vfp = sc->sc_mode.vsync_start - sc->sc_mode.vdisplay;
17006785ff6SJared McNeill 	WR1(sc, HDMI_FC_VSYNCINDELAY, vfp);
17106785ff6SJared McNeill 
17206785ff6SJared McNeill 	hsync_len = (sc->sc_mode.hsync_end - sc->sc_mode.hsync_start);
17306785ff6SJared McNeill 	/* Set up HSYNC active pulse width (in pixel clks) */
17406785ff6SJared McNeill 	WR1(sc, HDMI_FC_HSYNCINWIDTH1, hsync_len >> 8);
17506785ff6SJared McNeill 	WR1(sc, HDMI_FC_HSYNCINWIDTH0, hsync_len);
17606785ff6SJared McNeill 
17706785ff6SJared McNeill 	/* Set up VSYNC active edge delay (in pixel clks) */
17806785ff6SJared McNeill 	WR1(sc, HDMI_FC_VSYNCINWIDTH, (sc->sc_mode.vsync_end - sc->sc_mode.vsync_start));
17906785ff6SJared McNeill }
18006785ff6SJared McNeill 
18106785ff6SJared McNeill static void
dwc_hdmi_phy_enable_power(struct dwc_hdmi_softc * sc,uint8_t enable)18206785ff6SJared McNeill dwc_hdmi_phy_enable_power(struct dwc_hdmi_softc *sc, uint8_t enable)
18306785ff6SJared McNeill {
18406785ff6SJared McNeill 	uint8_t reg;
18506785ff6SJared McNeill 
18606785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
18706785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_PDZ_MASK;
18806785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET);
18906785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
19006785ff6SJared McNeill }
19106785ff6SJared McNeill 
19206785ff6SJared McNeill static void
dwc_hdmi_phy_enable_tmds(struct dwc_hdmi_softc * sc,uint8_t enable)19306785ff6SJared McNeill dwc_hdmi_phy_enable_tmds(struct dwc_hdmi_softc *sc, uint8_t enable)
19406785ff6SJared McNeill {
19506785ff6SJared McNeill 	uint8_t reg;
19606785ff6SJared McNeill 
19706785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
19806785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK;
19906785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
20006785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
20106785ff6SJared McNeill }
20206785ff6SJared McNeill 
20306785ff6SJared McNeill static void
dwc_hdmi_phy_gen2_pddq(struct dwc_hdmi_softc * sc,uint8_t enable)20406785ff6SJared McNeill dwc_hdmi_phy_gen2_pddq(struct dwc_hdmi_softc *sc, uint8_t enable)
20506785ff6SJared McNeill {
20606785ff6SJared McNeill 	uint8_t reg;
20706785ff6SJared McNeill 
20806785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
20906785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
21006785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
21106785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
21206785ff6SJared McNeill }
21306785ff6SJared McNeill 
21406785ff6SJared McNeill static void
dwc_hdmi_phy_gen2_txpwron(struct dwc_hdmi_softc * sc,uint8_t enable)21506785ff6SJared McNeill dwc_hdmi_phy_gen2_txpwron(struct dwc_hdmi_softc *sc, uint8_t enable)
21606785ff6SJared McNeill {
21706785ff6SJared McNeill 	uint8_t reg;
21806785ff6SJared McNeill 
21906785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
22006785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
22106785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
22206785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
22306785ff6SJared McNeill }
22406785ff6SJared McNeill 
22506785ff6SJared McNeill static void
dwc_hdmi_phy_sel_data_en_pol(struct dwc_hdmi_softc * sc,uint8_t enable)22606785ff6SJared McNeill dwc_hdmi_phy_sel_data_en_pol(struct dwc_hdmi_softc *sc, uint8_t enable)
22706785ff6SJared McNeill {
22806785ff6SJared McNeill 	uint8_t reg;
22906785ff6SJared McNeill 
23006785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
23106785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK;
23206785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
23306785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
23406785ff6SJared McNeill }
23506785ff6SJared McNeill 
23606785ff6SJared McNeill static void
dwc_hdmi_phy_sel_interface_control(struct dwc_hdmi_softc * sc,uint8_t enable)23706785ff6SJared McNeill dwc_hdmi_phy_sel_interface_control(struct dwc_hdmi_softc *sc, uint8_t enable)
23806785ff6SJared McNeill {
23906785ff6SJared McNeill 	uint8_t reg;
24006785ff6SJared McNeill 
24106785ff6SJared McNeill 	reg = RD1(sc, HDMI_PHY_CONF0);
24206785ff6SJared McNeill 	reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK;
24306785ff6SJared McNeill 	reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
24406785ff6SJared McNeill 	WR1(sc, HDMI_PHY_CONF0, reg);
24506785ff6SJared McNeill }
24606785ff6SJared McNeill 
24706785ff6SJared McNeill static inline void
dwc_hdmi_phy_test_clear(struct dwc_hdmi_softc * sc,unsigned char bit)24806785ff6SJared McNeill dwc_hdmi_phy_test_clear(struct dwc_hdmi_softc *sc, unsigned char bit)
24906785ff6SJared McNeill {
25006785ff6SJared McNeill 	uint8_t val;
25106785ff6SJared McNeill 
25206785ff6SJared McNeill 	val = RD1(sc, HDMI_PHY_TST0);
25306785ff6SJared McNeill 	val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
25406785ff6SJared McNeill 	val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
25506785ff6SJared McNeill 		HDMI_PHY_TST0_TSTCLR_MASK;
25606785ff6SJared McNeill 	WR1(sc, HDMI_PHY_TST0, val);
25706785ff6SJared McNeill }
25806785ff6SJared McNeill 
25906785ff6SJared McNeill static void
dwc_hdmi_clear_overflow(struct dwc_hdmi_softc * sc)26006785ff6SJared McNeill dwc_hdmi_clear_overflow(struct dwc_hdmi_softc *sc)
26106785ff6SJared McNeill {
26206785ff6SJared McNeill 	int count;
26306785ff6SJared McNeill 	uint8_t val;
26406785ff6SJared McNeill 
26506785ff6SJared McNeill 	/* TMDS software reset */
26606785ff6SJared McNeill 	WR1(sc, HDMI_MC_SWRSTZ, (uint8_t)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ);
26706785ff6SJared McNeill 
26806785ff6SJared McNeill 	val = RD1(sc, HDMI_FC_INVIDCONF);
26906785ff6SJared McNeill 
27006785ff6SJared McNeill 	for (count = 0 ; count < 4 ; count++)
27106785ff6SJared McNeill 		WR1(sc, HDMI_FC_INVIDCONF, val);
27206785ff6SJared McNeill }
27306785ff6SJared McNeill 
27406785ff6SJared McNeill static int
dwc_hdmi_phy_configure(struct dwc_hdmi_softc * sc)27506785ff6SJared McNeill dwc_hdmi_phy_configure(struct dwc_hdmi_softc *sc)
27606785ff6SJared McNeill {
27706785ff6SJared McNeill 	uint8_t val;
27806785ff6SJared McNeill 	uint8_t msec;
27906785ff6SJared McNeill 
28006785ff6SJared McNeill 	WR1(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS);
28106785ff6SJared McNeill 
28206785ff6SJared McNeill 	/* gen2 tx power off */
28306785ff6SJared McNeill 	dwc_hdmi_phy_gen2_txpwron(sc, 0);
28406785ff6SJared McNeill 
28506785ff6SJared McNeill 	/* gen2 pddq */
28606785ff6SJared McNeill 	dwc_hdmi_phy_gen2_pddq(sc, 1);
28706785ff6SJared McNeill 
28806785ff6SJared McNeill 	/* PHY reset */
28906785ff6SJared McNeill 	WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
29006785ff6SJared McNeill 	WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT);
29106785ff6SJared McNeill 
29206785ff6SJared McNeill 	WR1(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT);
29306785ff6SJared McNeill 
29406785ff6SJared McNeill 	dwc_hdmi_phy_test_clear(sc, 1);
29506785ff6SJared McNeill 	WR1(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
29606785ff6SJared McNeill 	dwc_hdmi_phy_test_clear(sc, 0);
29706785ff6SJared McNeill 
29806785ff6SJared McNeill 	/*
29906785ff6SJared McNeill 	 * Following initialization are for 8bit per color case
30006785ff6SJared McNeill 	 */
30106785ff6SJared McNeill 
30206785ff6SJared McNeill 	/*
30306785ff6SJared McNeill 	 * PLL/MPLL config, see section 24.7.22 in TRM
30406785ff6SJared McNeill 	 *  config, see section 24.7.22
30506785ff6SJared McNeill 	 */
30606785ff6SJared McNeill 	if (sc->sc_mode.dot_clock*1000 <= 45250000) {
30706785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_45_25, HDMI_PHY_I2C_CPCE_CTRL);
30806785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, GMPCTRL_45_25, HDMI_PHY_I2C_GMPCTRL);
30906785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 92500000) {
31006785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_92_50, HDMI_PHY_I2C_CPCE_CTRL);
31106785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, GMPCTRL_92_50, HDMI_PHY_I2C_GMPCTRL);
31206785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 185000000) {
31306785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_185, HDMI_PHY_I2C_CPCE_CTRL);
31406785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, GMPCTRL_185, HDMI_PHY_I2C_GMPCTRL);
31506785ff6SJared McNeill 	} else {
31606785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_370, HDMI_PHY_I2C_CPCE_CTRL);
31706785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, GMPCTRL_370, HDMI_PHY_I2C_GMPCTRL);
31806785ff6SJared McNeill 	}
31906785ff6SJared McNeill 
32006785ff6SJared McNeill 	/*
32106785ff6SJared McNeill 	 * Values described in TRM section 34.9.2 PLL/MPLL Generic
32206785ff6SJared McNeill 	 *    Configuration Settings. Table 34-23.
32306785ff6SJared McNeill 	 */
32406785ff6SJared McNeill 	if (sc->sc_mode.dot_clock*1000 <= 54000000) {
32506785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
32606785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 58400000) {
32706785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
32806785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 72000000) {
32906785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
33006785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 74250000) {
33106785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
33206785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 118800000) {
33306785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
33406785ff6SJared McNeill 	} else if (sc->sc_mode.dot_clock*1000 <= 216000000) {
33506785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
33606785ff6SJared McNeill 	} else {
33706785ff6SJared McNeill 		panic("Unsupported mode\n");
33806785ff6SJared McNeill 	}
33906785ff6SJared McNeill 
34006785ff6SJared McNeill 	dwc_hdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL);
34106785ff6SJared McNeill 	dwc_hdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL);
34206785ff6SJared McNeill 	/* RESISTANCE TERM 133 Ohm */
34306785ff6SJared McNeill 	dwc_hdmi_phy_i2c_write(sc, TXTERM_133, HDMI_PHY_I2C_TXTERM);
34406785ff6SJared McNeill 
34506785ff6SJared McNeill 	/* REMOVE CLK TERM */
34606785ff6SJared McNeill 	dwc_hdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL);
34706785ff6SJared McNeill 
34806785ff6SJared McNeill 	if (sc->sc_mode.dot_clock*1000 > 148500000) {
34906785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
35006785ff6SJared McNeill 		    CKSYMTXCTRL_TX_TRBON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL);
35106785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(9) | VLEVCTRL_CK_LVL(9),
35206785ff6SJared McNeill 		    HDMI_PHY_I2C_VLEVCTRL);
35306785ff6SJared McNeill 	} else {
35406785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
35506785ff6SJared McNeill 		    CKSYMTXCTRL_TX_TRAON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL);
35606785ff6SJared McNeill 		dwc_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(13) | VLEVCTRL_CK_LVL(13),
35706785ff6SJared McNeill 		    HDMI_PHY_I2C_VLEVCTRL);
35806785ff6SJared McNeill 	}
35906785ff6SJared McNeill 
36006785ff6SJared McNeill 	dwc_hdmi_phy_enable_power(sc, 1);
36106785ff6SJared McNeill 
36206785ff6SJared McNeill 	/* toggle TMDS enable */
36306785ff6SJared McNeill 	dwc_hdmi_phy_enable_tmds(sc, 0);
36406785ff6SJared McNeill 	dwc_hdmi_phy_enable_tmds(sc, 1);
36506785ff6SJared McNeill 
36606785ff6SJared McNeill 	/* gen2 tx power on */
36706785ff6SJared McNeill 	dwc_hdmi_phy_gen2_txpwron(sc, 1);
36806785ff6SJared McNeill 	dwc_hdmi_phy_gen2_pddq(sc, 0);
36906785ff6SJared McNeill 
37006785ff6SJared McNeill 	/*Wait for PHY PLL lock */
37106785ff6SJared McNeill 	msec = 4;
37206785ff6SJared McNeill 	val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
37306785ff6SJared McNeill 	while (val == 0) {
37406785ff6SJared McNeill 		DELAY(1000);
37506785ff6SJared McNeill 		if (msec-- == 0) {
37606785ff6SJared McNeill 			device_printf(sc->sc_dev, "PHY PLL not locked\n");
37706785ff6SJared McNeill 			return (-1);
37806785ff6SJared McNeill 		}
37906785ff6SJared McNeill 		val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
38006785ff6SJared McNeill 	}
38106785ff6SJared McNeill 
38206785ff6SJared McNeill 	return true;
38306785ff6SJared McNeill }
38406785ff6SJared McNeill 
38506785ff6SJared McNeill static void
dwc_hdmi_phy_init(struct dwc_hdmi_softc * sc)38606785ff6SJared McNeill dwc_hdmi_phy_init(struct dwc_hdmi_softc *sc)
38706785ff6SJared McNeill {
38806785ff6SJared McNeill 	int i;
38906785ff6SJared McNeill 
39006785ff6SJared McNeill 	/* HDMI Phy spec says to do the phy initialization sequence twice */
39106785ff6SJared McNeill 	for (i = 0 ; i < 2 ; i++) {
39206785ff6SJared McNeill 		dwc_hdmi_phy_sel_data_en_pol(sc, 1);
39306785ff6SJared McNeill 		dwc_hdmi_phy_sel_interface_control(sc, 0);
39406785ff6SJared McNeill 		dwc_hdmi_phy_enable_tmds(sc, 0);
39506785ff6SJared McNeill 		dwc_hdmi_phy_enable_power(sc, 0);
39606785ff6SJared McNeill 
39706785ff6SJared McNeill 		/* Enable CSC */
39806785ff6SJared McNeill 		dwc_hdmi_phy_configure(sc);
39906785ff6SJared McNeill 	}
40006785ff6SJared McNeill }
40106785ff6SJared McNeill 
40206785ff6SJared McNeill static void
dwc_hdmi_enable_video_path(struct dwc_hdmi_softc * sc)40306785ff6SJared McNeill dwc_hdmi_enable_video_path(struct dwc_hdmi_softc *sc)
40406785ff6SJared McNeill {
40506785ff6SJared McNeill 	uint8_t clkdis;
40606785ff6SJared McNeill 
40706785ff6SJared McNeill 	/*
40806785ff6SJared McNeill 	 * Control period timing
40906785ff6SJared McNeill 	 * Values are minimal according to HDMI spec 1.4a
41006785ff6SJared McNeill 	 */
41106785ff6SJared McNeill 	WR1(sc, HDMI_FC_CTRLDUR, 12);
41206785ff6SJared McNeill 	WR1(sc, HDMI_FC_EXCTRLDUR, 32);
41306785ff6SJared McNeill 	WR1(sc, HDMI_FC_EXCTRLSPAC, 1);
41406785ff6SJared McNeill 
41506785ff6SJared McNeill 	/*
41606785ff6SJared McNeill 	 * Bits to fill data lines not used to transmit preamble
41706785ff6SJared McNeill 	 * for channels 0, 1, and 2 respectively
41806785ff6SJared McNeill 	 */
41906785ff6SJared McNeill 	WR1(sc, HDMI_FC_CH0PREAM, 0x0B);
42006785ff6SJared McNeill 	WR1(sc, HDMI_FC_CH1PREAM, 0x16);
42106785ff6SJared McNeill 	WR1(sc, HDMI_FC_CH2PREAM, 0x21);
42206785ff6SJared McNeill 
42306785ff6SJared McNeill 	/* Save CEC clock */
42406785ff6SJared McNeill 	clkdis = RD1(sc, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE;
42506785ff6SJared McNeill 	clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
42606785ff6SJared McNeill 
42706785ff6SJared McNeill 	/* Enable pixel clock and tmds data path */
42806785ff6SJared McNeill 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
42906785ff6SJared McNeill 	WR1(sc, HDMI_MC_CLKDIS, clkdis);
43006785ff6SJared McNeill 
43106785ff6SJared McNeill 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
43206785ff6SJared McNeill 	WR1(sc, HDMI_MC_CLKDIS, clkdis);
43306785ff6SJared McNeill }
43406785ff6SJared McNeill 
43506785ff6SJared McNeill static void
dwc_hdmi_configure_audio(struct dwc_hdmi_softc * sc)4366443acaaSJared McNeill dwc_hdmi_configure_audio(struct dwc_hdmi_softc *sc)
4376443acaaSJared McNeill {
4386443acaaSJared McNeill 	unsigned int n;
4396443acaaSJared McNeill 	uint8_t val;
4406443acaaSJared McNeill 
4416443acaaSJared McNeill 	if (sc->sc_has_audio == 0)
4426443acaaSJared McNeill 		return;
4436443acaaSJared McNeill 
4446443acaaSJared McNeill 	/* The following values are for 48 kHz */
4456443acaaSJared McNeill 	switch (sc->sc_mode.dot_clock) {
4466443acaaSJared McNeill 	case 25170:
4476443acaaSJared McNeill 		n = 6864;
4486443acaaSJared McNeill 		break;
4496443acaaSJared McNeill 	case 27020:
4506443acaaSJared McNeill 		n = 6144;
4516443acaaSJared McNeill 		break;
4526443acaaSJared McNeill 	case 74170:
4536443acaaSJared McNeill 		n = 11648;
4546443acaaSJared McNeill 		break;
4556443acaaSJared McNeill 	case 148350:
4566443acaaSJared McNeill 		n = 5824;
4576443acaaSJared McNeill 		break;
4586443acaaSJared McNeill 	default:
4596443acaaSJared McNeill 		n = 6144;
4606443acaaSJared McNeill 		break;
4616443acaaSJared McNeill 	}
4626443acaaSJared McNeill 
4636443acaaSJared McNeill 	WR1(sc, HDMI_AUD_N1, (n >> 0) & 0xff);
4646443acaaSJared McNeill 	WR1(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
4656443acaaSJared McNeill 	WR1(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
4666443acaaSJared McNeill 
4676443acaaSJared McNeill 	val = RD1(sc, HDMI_AUD_CTS3);
4686443acaaSJared McNeill 	val &= ~(HDMI_AUD_CTS3_N_SHIFT_MASK | HDMI_AUD_CTS3_CTS_MANUAL);
4696443acaaSJared McNeill 	WR1(sc, HDMI_AUD_CTS3, val);
4706443acaaSJared McNeill 
4716443acaaSJared McNeill 	val = RD1(sc, HDMI_AUD_CONF0);
4726443acaaSJared McNeill 	val &= ~HDMI_AUD_CONF0_INTERFACE_MASK;
4736443acaaSJared McNeill 	val |= HDMI_AUD_CONF0_INTERFACE_IIS;
4746443acaaSJared McNeill 	val &= ~HDMI_AUD_CONF0_I2SINEN_MASK;
4756443acaaSJared McNeill 	val |= HDMI_AUD_CONF0_I2SINEN_CH2;
4766443acaaSJared McNeill 	WR1(sc, HDMI_AUD_CONF0, val);
4776443acaaSJared McNeill 
4786443acaaSJared McNeill 	val = RD1(sc, HDMI_AUD_CONF1);
4796443acaaSJared McNeill 	val &= ~HDMI_AUD_CONF1_DATAMODE_MASK;
4806443acaaSJared McNeill 	val |= HDMI_AUD_CONF1_DATAMODE_IIS;
4816443acaaSJared McNeill 	val &= ~HDMI_AUD_CONF1_DATWIDTH_MASK;
4826443acaaSJared McNeill 	val |= HDMI_AUD_CONF1_DATWIDTH_16BIT;
4836443acaaSJared McNeill 	WR1(sc, HDMI_AUD_CONF1, val);
4846443acaaSJared McNeill 
4856443acaaSJared McNeill 	WR1(sc, HDMI_AUD_INPUTCLKFS, HDMI_AUD_INPUTCLKFS_64);
4866443acaaSJared McNeill 
4876443acaaSJared McNeill 	WR1(sc, HDMI_FC_AUDICONF0, 1 << 4);	/* CC=1 */
4886443acaaSJared McNeill 	WR1(sc, HDMI_FC_AUDICONF1, 0);
4896443acaaSJared McNeill 	WR1(sc, HDMI_FC_AUDICONF2, 0);		/* CA=0 */
4906443acaaSJared McNeill 	WR1(sc, HDMI_FC_AUDICONF3, 0);
4916443acaaSJared McNeill 	WR1(sc, HDMI_FC_AUDSV, 0xee);		/* channels valid */
4926443acaaSJared McNeill 
4936443acaaSJared McNeill 	/* Enable audio clock */
4946443acaaSJared McNeill 	val = RD1(sc, HDMI_MC_CLKDIS);
4956443acaaSJared McNeill 	val &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
4966443acaaSJared McNeill 	WR1(sc, HDMI_MC_CLKDIS, val);
4976443acaaSJared McNeill }
4986443acaaSJared McNeill 
4996443acaaSJared McNeill static void
dwc_hdmi_video_packetize(struct dwc_hdmi_softc * sc)50006785ff6SJared McNeill dwc_hdmi_video_packetize(struct dwc_hdmi_softc *sc)
50106785ff6SJared McNeill {
50206785ff6SJared McNeill 	unsigned int color_depth = 0;
50306785ff6SJared McNeill 	unsigned int remap_size = HDMI_VP_REMAP_YCC422_16BIT;
50406785ff6SJared McNeill 	unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
50506785ff6SJared McNeill 	uint8_t val;
50606785ff6SJared McNeill 
50706785ff6SJared McNeill 	output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
50806785ff6SJared McNeill 	color_depth = 4;
50906785ff6SJared McNeill 
51006785ff6SJared McNeill 	/* set the packetizer registers */
51106785ff6SJared McNeill 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
51206785ff6SJared McNeill 		HDMI_VP_PR_CD_COLOR_DEPTH_MASK);
51306785ff6SJared McNeill 	WR1(sc, HDMI_VP_PR_CD, val);
51406785ff6SJared McNeill 
51506785ff6SJared McNeill 	val = RD1(sc, HDMI_VP_STUFF);
51606785ff6SJared McNeill 	val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK;
51706785ff6SJared McNeill 	val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE;
51806785ff6SJared McNeill 	WR1(sc, HDMI_VP_STUFF, val);
51906785ff6SJared McNeill 
52006785ff6SJared McNeill 	val = RD1(sc, HDMI_VP_CONF);
52106785ff6SJared McNeill 	val &= ~(HDMI_VP_CONF_PR_EN_MASK |
52206785ff6SJared McNeill 		HDMI_VP_CONF_BYPASS_SELECT_MASK);
52306785ff6SJared McNeill 	val |= HDMI_VP_CONF_PR_EN_DISABLE |
52406785ff6SJared McNeill 		HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
52506785ff6SJared McNeill 	WR1(sc, HDMI_VP_CONF, val);
52606785ff6SJared McNeill 
52706785ff6SJared McNeill 	val = RD1(sc, HDMI_VP_STUFF);
52806785ff6SJared McNeill 	val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK;
52906785ff6SJared McNeill 	val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET;
53006785ff6SJared McNeill 	WR1(sc, HDMI_VP_STUFF, val);
53106785ff6SJared McNeill 
53206785ff6SJared McNeill 	WR1(sc, HDMI_VP_REMAP, remap_size);
53306785ff6SJared McNeill 
53406785ff6SJared McNeill 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
53506785ff6SJared McNeill 		val = RD1(sc, HDMI_VP_CONF);
53606785ff6SJared McNeill 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
53706785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_ENMASK |
53806785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_MASK);
53906785ff6SJared McNeill 		val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
54006785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_ENABLE |
54106785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_DISABLE;
54206785ff6SJared McNeill 		WR1(sc, HDMI_VP_CONF, val);
54306785ff6SJared McNeill 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
54406785ff6SJared McNeill 		val = RD1(sc, HDMI_VP_CONF);
54506785ff6SJared McNeill 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
54606785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_ENMASK |
54706785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_MASK);
54806785ff6SJared McNeill 		val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
54906785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_DISABLE |
55006785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_ENABLE;
55106785ff6SJared McNeill 		WR1(sc, HDMI_VP_CONF, val);
55206785ff6SJared McNeill 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
55306785ff6SJared McNeill 		val = RD1(sc, HDMI_VP_CONF);
55406785ff6SJared McNeill 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
55506785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_ENMASK |
55606785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_MASK);
55706785ff6SJared McNeill 		val |= HDMI_VP_CONF_BYPASS_EN_ENABLE |
55806785ff6SJared McNeill 			HDMI_VP_CONF_PP_EN_DISABLE |
55906785ff6SJared McNeill 			HDMI_VP_CONF_YCC422_EN_DISABLE;
56006785ff6SJared McNeill 		WR1(sc, HDMI_VP_CONF, val);
56106785ff6SJared McNeill 	} else {
56206785ff6SJared McNeill 		return;
56306785ff6SJared McNeill 	}
56406785ff6SJared McNeill 
56506785ff6SJared McNeill 	val = RD1(sc, HDMI_VP_STUFF);
56606785ff6SJared McNeill 	val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK |
56706785ff6SJared McNeill 		HDMI_VP_STUFF_YCC422_STUFFING_MASK);
56806785ff6SJared McNeill 	val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
56906785ff6SJared McNeill 		HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE;
57006785ff6SJared McNeill 	WR1(sc, HDMI_VP_STUFF, val);
57106785ff6SJared McNeill 
57206785ff6SJared McNeill 	val = RD1(sc, HDMI_VP_CONF);
57306785ff6SJared McNeill 	val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK;
57406785ff6SJared McNeill 	val |= output_select;
57506785ff6SJared McNeill 	WR1(sc, HDMI_VP_CONF, val);
57606785ff6SJared McNeill }
57706785ff6SJared McNeill 
57806785ff6SJared McNeill static void
dwc_hdmi_video_sample(struct dwc_hdmi_softc * sc)57906785ff6SJared McNeill dwc_hdmi_video_sample(struct dwc_hdmi_softc *sc)
58006785ff6SJared McNeill {
58106785ff6SJared McNeill 	int color_format;
58206785ff6SJared McNeill 	uint8_t val;
58306785ff6SJared McNeill 
58406785ff6SJared McNeill 	color_format = 0x01;
58506785ff6SJared McNeill 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
58606785ff6SJared McNeill 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
58706785ff6SJared McNeill 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
58806785ff6SJared McNeill 	WR1(sc, HDMI_TX_INVID0, val);
58906785ff6SJared McNeill 
59006785ff6SJared McNeill 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
59106785ff6SJared McNeill 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
59206785ff6SJared McNeill 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
59306785ff6SJared McNeill 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
59406785ff6SJared McNeill 	WR1(sc, HDMI_TX_INSTUFFING, val);
59506785ff6SJared McNeill 	WR1(sc, HDMI_TX_GYDATA0, 0x0);
59606785ff6SJared McNeill 	WR1(sc, HDMI_TX_GYDATA1, 0x0);
59706785ff6SJared McNeill 	WR1(sc, HDMI_TX_RCRDATA0, 0x0);
59806785ff6SJared McNeill 	WR1(sc, HDMI_TX_RCRDATA1, 0x0);
59906785ff6SJared McNeill 	WR1(sc, HDMI_TX_BCBDATA0, 0x0);
60006785ff6SJared McNeill 	WR1(sc, HDMI_TX_BCBDATA1, 0x0);
60106785ff6SJared McNeill }
60206785ff6SJared McNeill 
60306785ff6SJared McNeill static void
dwc_hdmi_tx_hdcp_config(struct dwc_hdmi_softc * sc)60406785ff6SJared McNeill dwc_hdmi_tx_hdcp_config(struct dwc_hdmi_softc *sc)
60506785ff6SJared McNeill {
60606785ff6SJared McNeill 	uint8_t de, val;
60706785ff6SJared McNeill 
60806785ff6SJared McNeill 	de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
60906785ff6SJared McNeill 
61006785ff6SJared McNeill 	/* Disable RX detect */
61106785ff6SJared McNeill 	val = RD1(sc, HDMI_A_HDCPCFG0);
61206785ff6SJared McNeill 	val &= ~HDMI_A_HDCPCFG0_RXDETECT_MASK;
61306785ff6SJared McNeill 	val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE;
61406785ff6SJared McNeill 	WR1(sc, HDMI_A_HDCPCFG0, val);
61506785ff6SJared McNeill 
61606785ff6SJared McNeill 	/* Set polarity */
61706785ff6SJared McNeill 	val = RD1(sc, HDMI_A_VIDPOLCFG);
61806785ff6SJared McNeill 	val &= ~HDMI_A_VIDPOLCFG_DATAENPOL_MASK;
61906785ff6SJared McNeill 	val |= de;
62006785ff6SJared McNeill 	WR1(sc, HDMI_A_VIDPOLCFG, val);
62106785ff6SJared McNeill 
62206785ff6SJared McNeill 	/* Disable encryption */
62306785ff6SJared McNeill 	val = RD1(sc, HDMI_A_HDCPCFG1);
62406785ff6SJared McNeill 	val &= ~HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK;
62506785ff6SJared McNeill 	val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE;
62606785ff6SJared McNeill 	WR1(sc, HDMI_A_HDCPCFG1, val);
62706785ff6SJared McNeill }
62806785ff6SJared McNeill 
62906785ff6SJared McNeill static int
dwc_hdmi_set_mode(struct dwc_hdmi_softc * sc)63006785ff6SJared McNeill dwc_hdmi_set_mode(struct dwc_hdmi_softc *sc)
63106785ff6SJared McNeill {
63206785ff6SJared McNeill 
6336443acaaSJared McNeill 	/* XXX */
6346443acaaSJared McNeill 	sc->sc_has_audio = 1;
6356443acaaSJared McNeill 
63606785ff6SJared McNeill 	dwc_hdmi_disable_overflow_interrupts(sc);
63706785ff6SJared McNeill 	dwc_hdmi_av_composer(sc);
63806785ff6SJared McNeill 	dwc_hdmi_phy_init(sc);
63906785ff6SJared McNeill 	dwc_hdmi_enable_video_path(sc);
6406443acaaSJared McNeill 	dwc_hdmi_configure_audio(sc);
6416443acaaSJared McNeill 	/* TODO:  dwc_hdmi_config_avi(sc); */
64206785ff6SJared McNeill 	dwc_hdmi_video_packetize(sc);
64306785ff6SJared McNeill 	/* TODO:  dwc_hdmi_video_csc(sc); */
64406785ff6SJared McNeill 	dwc_hdmi_video_sample(sc);
64506785ff6SJared McNeill 	dwc_hdmi_tx_hdcp_config(sc);
64606785ff6SJared McNeill 	dwc_hdmi_clear_overflow(sc);
64706785ff6SJared McNeill 
64806785ff6SJared McNeill 	return (0);
64906785ff6SJared McNeill }
65006785ff6SJared McNeill 
65106785ff6SJared McNeill static int
hdmi_edid_read(struct dwc_hdmi_softc * sc,int block,uint8_t ** edid,uint32_t * edid_len)6526443acaaSJared McNeill hdmi_edid_read(struct dwc_hdmi_softc *sc, int block, uint8_t **edid,
6536443acaaSJared McNeill     uint32_t *edid_len)
65406785ff6SJared McNeill {
65506785ff6SJared McNeill 	device_t i2c_dev;
65606785ff6SJared McNeill 	int result;
6576443acaaSJared McNeill 	uint8_t addr = block & 1 ? EDID_LENGTH : 0;
6586443acaaSJared McNeill 	uint8_t segment = block >> 1;
659cbc596d6SOleksandr Tymoshenko 	/*
660cbc596d6SOleksandr Tymoshenko 	 * Some devices do not support E-DDC so attempt
661cbc596d6SOleksandr Tymoshenko 	 * writing segment address only if it's neccessary
662cbc596d6SOleksandr Tymoshenko 	 */
663cbc596d6SOleksandr Tymoshenko 	unsigned char xfers = segment ? 3 : 2;
66406785ff6SJared McNeill 	struct iic_msg msg[] = {
6656443acaaSJared McNeill 		{ I2C_DDC_SEGADDR, IIC_M_WR, 1, &segment },
6666443acaaSJared McNeill 		{ I2C_DDC_ADDR, IIC_M_WR, 1, &addr },
6676443acaaSJared McNeill 		{ I2C_DDC_ADDR, IIC_M_RD, EDID_LENGTH, sc->sc_edid }
66806785ff6SJared McNeill 	};
66906785ff6SJared McNeill 
67006785ff6SJared McNeill 	*edid = NULL;
67106785ff6SJared McNeill 	*edid_len = 0;
67206785ff6SJared McNeill 	i2c_dev = NULL;
67306785ff6SJared McNeill 
67406785ff6SJared McNeill 	if (sc->sc_get_i2c_dev != NULL)
67506785ff6SJared McNeill 		i2c_dev = sc->sc_get_i2c_dev(sc->sc_dev);
67606785ff6SJared McNeill 	if (!i2c_dev) {
67706785ff6SJared McNeill 		device_printf(sc->sc_dev, "no DDC device found\n");
67806785ff6SJared McNeill 		return (ENXIO);
67906785ff6SJared McNeill 	}
68006785ff6SJared McNeill 
6816443acaaSJared McNeill 	if (bootverbose)
6826443acaaSJared McNeill 		device_printf(sc->sc_dev,
6836443acaaSJared McNeill 		    "reading EDID from %s, block %d, addr %02x\n",
6846443acaaSJared McNeill 		    device_get_nameunit(i2c_dev), block, I2C_DDC_ADDR/2);
68506785ff6SJared McNeill 
68606785ff6SJared McNeill 	result = iicbus_request_bus(i2c_dev, sc->sc_dev, IIC_INTRWAIT);
68706785ff6SJared McNeill 
68806785ff6SJared McNeill 	if (result) {
68906785ff6SJared McNeill 		device_printf(sc->sc_dev, "failed to request i2c bus: %d\n", result);
69006785ff6SJared McNeill 		return (result);
69106785ff6SJared McNeill 	}
69206785ff6SJared McNeill 
693cbc596d6SOleksandr Tymoshenko 	result = iicbus_transfer(i2c_dev, &msg[3 - xfers], xfers);
69406785ff6SJared McNeill 	iicbus_release_bus(i2c_dev, sc->sc_dev);
69506785ff6SJared McNeill 
69606785ff6SJared McNeill 	if (result) {
69706785ff6SJared McNeill 		device_printf(sc->sc_dev, "i2c transfer failed: %d\n", result);
69806785ff6SJared McNeill 		return (result);
69906785ff6SJared McNeill 	} else {
70006785ff6SJared McNeill 		*edid_len = sc->sc_edid_len;
70106785ff6SJared McNeill 		*edid = sc->sc_edid;
70206785ff6SJared McNeill 	}
70306785ff6SJared McNeill 
70406785ff6SJared McNeill 	return (result);
70506785ff6SJared McNeill }
70606785ff6SJared McNeill 
70706785ff6SJared McNeill static void
dwc_hdmi_detect_cable(void * arg)70806785ff6SJared McNeill dwc_hdmi_detect_cable(void *arg)
70906785ff6SJared McNeill {
71006785ff6SJared McNeill 	struct dwc_hdmi_softc *sc;
71106785ff6SJared McNeill 	uint32_t stat;
71206785ff6SJared McNeill 
71306785ff6SJared McNeill 	sc = arg;
71406785ff6SJared McNeill 
71506785ff6SJared McNeill 	stat = RD1(sc, HDMI_IH_PHY_STAT0);
71606785ff6SJared McNeill 	if ((stat & HDMI_IH_PHY_STAT0_HPD) != 0) {
71706785ff6SJared McNeill 		EVENTHANDLER_INVOKE(hdmi_event, sc->sc_dev,
71806785ff6SJared McNeill 		    HDMI_EVENT_CONNECTED);
71906785ff6SJared McNeill 	}
72006785ff6SJared McNeill 
72106785ff6SJared McNeill 	/* Finished with the interrupt hook */
72206785ff6SJared McNeill 	config_intrhook_disestablish(&sc->sc_mode_hook);
72306785ff6SJared McNeill }
72406785ff6SJared McNeill 
72506785ff6SJared McNeill int
dwc_hdmi_init(device_t dev)72606785ff6SJared McNeill dwc_hdmi_init(device_t dev)
72706785ff6SJared McNeill {
72806785ff6SJared McNeill 	struct dwc_hdmi_softc *sc;
72906785ff6SJared McNeill 	int err;
73006785ff6SJared McNeill 
73106785ff6SJared McNeill 	sc = device_get_softc(dev);
73206785ff6SJared McNeill 	err = 0;
73306785ff6SJared McNeill 
73406785ff6SJared McNeill 	sc->sc_edid = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO);
73506785ff6SJared McNeill 	sc->sc_edid_len = EDID_LENGTH;
73606785ff6SJared McNeill 
73706785ff6SJared McNeill 	device_printf(sc->sc_dev, "HDMI controller %02x:%02x:%02x:%02x\n",
73806785ff6SJared McNeill 	    RD1(sc, HDMI_DESIGN_ID), RD1(sc, HDMI_REVISION_ID),
73906785ff6SJared McNeill 	    RD1(sc, HDMI_PRODUCT_ID0), RD1(sc, HDMI_PRODUCT_ID1));
74006785ff6SJared McNeill 
74106785ff6SJared McNeill 	WR1(sc, HDMI_PHY_POL0, HDMI_PHY_POL0_HPD);
74206785ff6SJared McNeill 	WR1(sc, HDMI_IH_PHY_STAT0, HDMI_IH_PHY_STAT0_HPD);
74306785ff6SJared McNeill 
74406785ff6SJared McNeill 	sc->sc_mode_hook.ich_func = dwc_hdmi_detect_cable;
74506785ff6SJared McNeill 	sc->sc_mode_hook.ich_arg = sc;
74606785ff6SJared McNeill 	if (config_intrhook_establish(&sc->sc_mode_hook) != 0) {
74706785ff6SJared McNeill 		err = ENOMEM;
74806785ff6SJared McNeill 		goto out;
74906785ff6SJared McNeill 	}
75006785ff6SJared McNeill 
75106785ff6SJared McNeill out:
75206785ff6SJared McNeill 
75306785ff6SJared McNeill 	if (err != 0) {
75406785ff6SJared McNeill 		free(sc->sc_edid, M_DEVBUF);
75506785ff6SJared McNeill 		sc->sc_edid = NULL;
75606785ff6SJared McNeill 	}
75706785ff6SJared McNeill 
75806785ff6SJared McNeill 	return (err);
75906785ff6SJared McNeill }
76006785ff6SJared McNeill 
7616443acaaSJared McNeill static int
dwc_hdmi_detect_hdmi_vsdb(uint8_t * edid)7626443acaaSJared McNeill dwc_hdmi_detect_hdmi_vsdb(uint8_t *edid)
7636443acaaSJared McNeill {
7646443acaaSJared McNeill 	int off, p, btag, blen;
7656443acaaSJared McNeill 
7666443acaaSJared McNeill 	if (edid[EXT_TAG] != CEA_TAG_ID)
7676443acaaSJared McNeill 		return (0);
7686443acaaSJared McNeill 
7696443acaaSJared McNeill 	off = edid[CEA_DATA_OFF];
7706443acaaSJared McNeill 
7716443acaaSJared McNeill 	/* CEA data block collection starts at byte 4 */
7726443acaaSJared McNeill 	if (off <= CEA_DATA_START)
7736443acaaSJared McNeill 		return (0);
7746443acaaSJared McNeill 
7756443acaaSJared McNeill 	/* Parse the CEA data blocks */
7766443acaaSJared McNeill 	for (p = CEA_DATA_START; p < off;) {
7776443acaaSJared McNeill 		btag = BLOCK_TAG(edid[p]);
7786443acaaSJared McNeill 		blen = BLOCK_LEN(edid[p]);
7796443acaaSJared McNeill 
7806443acaaSJared McNeill 		/* Make sure the length is sane */
7816443acaaSJared McNeill 		if (p + blen + 1 > off)
7826443acaaSJared McNeill 			break;
7836443acaaSJared McNeill 
7846443acaaSJared McNeill 		/* Look for a VSDB with the HDMI 24-bit IEEE registration ID */
7856443acaaSJared McNeill 		if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN &&
7866443acaaSJared McNeill 		    memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0)
7876443acaaSJared McNeill 			return (1);
7886443acaaSJared McNeill 
7896443acaaSJared McNeill 		/* Next data block */
7906443acaaSJared McNeill 		p += (1 + blen);
7916443acaaSJared McNeill 	}
7926443acaaSJared McNeill 
7936443acaaSJared McNeill 	/* Not found */
7946443acaaSJared McNeill 	return (0);
7956443acaaSJared McNeill }
7966443acaaSJared McNeill 
7976443acaaSJared McNeill static void
dwc_hdmi_detect_hdmi(struct dwc_hdmi_softc * sc)7986443acaaSJared McNeill dwc_hdmi_detect_hdmi(struct dwc_hdmi_softc *sc)
7996443acaaSJared McNeill {
8006443acaaSJared McNeill 	uint8_t *edid;
8016443acaaSJared McNeill 	uint32_t edid_len;
8026443acaaSJared McNeill 	int block;
8036443acaaSJared McNeill 
8046443acaaSJared McNeill 	sc->sc_has_audio = 0;
8056443acaaSJared McNeill 
8066443acaaSJared McNeill 	/* Scan through extension blocks, looking for a CEA-861 block */
8076443acaaSJared McNeill 	for (block = 1; block <= sc->sc_edid_info.edid_ext_block_count;
8086443acaaSJared McNeill 	    block++) {
8096443acaaSJared McNeill 		if (hdmi_edid_read(sc, block, &edid, &edid_len) != 0)
8106443acaaSJared McNeill 			return;
8116443acaaSJared McNeill 		if (dwc_hdmi_detect_hdmi_vsdb(edid) != 0) {
8126443acaaSJared McNeill 			if (bootverbose)
8136443acaaSJared McNeill 				device_printf(sc->sc_dev,
8146443acaaSJared McNeill 				    "enabling audio support\n");
8156443acaaSJared McNeill 			sc->sc_has_audio =
8166443acaaSJared McNeill 			    (edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0;
8176443acaaSJared McNeill 			return;
8186443acaaSJared McNeill 		}
8196443acaaSJared McNeill 	}
8206443acaaSJared McNeill }
8216443acaaSJared McNeill 
82206785ff6SJared McNeill int
dwc_hdmi_get_edid(device_t dev,uint8_t ** edid,uint32_t * edid_len)82306785ff6SJared McNeill dwc_hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
82406785ff6SJared McNeill {
8256443acaaSJared McNeill 	struct dwc_hdmi_softc *sc;
8266443acaaSJared McNeill 	int error;
82706785ff6SJared McNeill 
8286443acaaSJared McNeill 	sc = device_get_softc(dev);
8296443acaaSJared McNeill 
8306443acaaSJared McNeill 	memset(&sc->sc_edid_info, 0, sizeof(sc->sc_edid_info));
8316443acaaSJared McNeill 
8326443acaaSJared McNeill 	error = hdmi_edid_read(sc, 0, edid, edid_len);
8336443acaaSJared McNeill 	if (error != 0)
8346443acaaSJared McNeill 		return (error);
8356443acaaSJared McNeill 
8366443acaaSJared McNeill 	edid_parse(*edid, &sc->sc_edid_info);
8376443acaaSJared McNeill 
8386443acaaSJared McNeill 	return (0);
83906785ff6SJared McNeill }
84006785ff6SJared McNeill 
84106785ff6SJared McNeill int
dwc_hdmi_set_videomode(device_t dev,const struct videomode * mode)84206785ff6SJared McNeill dwc_hdmi_set_videomode(device_t dev, const struct videomode *mode)
84306785ff6SJared McNeill {
84406785ff6SJared McNeill 	struct dwc_hdmi_softc *sc;
84506785ff6SJared McNeill 
84606785ff6SJared McNeill 	sc = device_get_softc(dev);
84706785ff6SJared McNeill 	memcpy(&sc->sc_mode, mode, sizeof(*mode));
84806785ff6SJared McNeill 
8496443acaaSJared McNeill 	dwc_hdmi_detect_hdmi(sc);
8506443acaaSJared McNeill 
85106785ff6SJared McNeill 	dwc_hdmi_set_mode(sc);
85206785ff6SJared McNeill 
85306785ff6SJared McNeill 	return (0);
85406785ff6SJared McNeill }
855