xref: /openbsd-src/sys/dev/ic/dwhdmi.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
1*f005ef32Sjsg /* $OpenBSD: dwhdmi.c,v 1.5 2024/01/16 23:37:50 jsg Exp $ */
26a2cdf39Skettenis /* $NetBSD: dw_hdmi.c,v 1.7 2019/12/22 23:23:32 thorpej Exp $ */
36a2cdf39Skettenis 
46a2cdf39Skettenis /*-
56a2cdf39Skettenis  * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
66a2cdf39Skettenis  * All rights reserved.
76a2cdf39Skettenis  *
86a2cdf39Skettenis  * Redistribution and use in source and binary forms, with or without
96a2cdf39Skettenis  * modification, are permitted provided that the following conditions
106a2cdf39Skettenis  * are met:
116a2cdf39Skettenis  * 1. Redistributions of source code must retain the above copyright
126a2cdf39Skettenis  *    notice, this list of conditions and the following disclaimer.
136a2cdf39Skettenis  * 2. Redistributions in binary form must reproduce the above copyright
146a2cdf39Skettenis  *    notice, this list of conditions and the following disclaimer in the
156a2cdf39Skettenis  *    documentation and/or other materials provided with the distribution.
166a2cdf39Skettenis  *
176a2cdf39Skettenis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
186a2cdf39Skettenis  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
196a2cdf39Skettenis  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
206a2cdf39Skettenis  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
216a2cdf39Skettenis  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
226a2cdf39Skettenis  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
236a2cdf39Skettenis  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
246a2cdf39Skettenis  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
256a2cdf39Skettenis  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
266a2cdf39Skettenis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276a2cdf39Skettenis  * SUCH DAMAGE.
286a2cdf39Skettenis  */
296a2cdf39Skettenis 
306a2cdf39Skettenis #include <sys/param.h>
316a2cdf39Skettenis #include <sys/device.h>
326a2cdf39Skettenis #include <sys/systm.h>
336a2cdf39Skettenis #include <sys/kernel.h>
346a2cdf39Skettenis 
356a2cdf39Skettenis #include <dev/ic/dwhdmi.h>
366a2cdf39Skettenis 
376a2cdf39Skettenis #include <dev/i2c/i2cvar.h>
38*f005ef32Sjsg #include <linux/i2c.h>
396a2cdf39Skettenis 
406a2cdf39Skettenis #ifdef notyet
416a2cdf39Skettenis #include <dev/audio/audio_dai.h>
426a2cdf39Skettenis #endif
436a2cdf39Skettenis 
44c349dbc7Sjsg #include <drm/drm_atomic.h>
45c349dbc7Sjsg #include <drm/drm_atomic_helper.h>
466a2cdf39Skettenis #include <drm/drm_crtc.h>
476a2cdf39Skettenis #include <drm/drm_crtc_helper.h>
486a2cdf39Skettenis #include <drm/drm_edid.h>
49c349dbc7Sjsg #include <drm/drm_probe_helper.h>
506a2cdf39Skettenis 
516a2cdf39Skettenis #define DDC_SEGMENT_ADDR	0x30
526a2cdf39Skettenis 
536a2cdf39Skettenis #define	HDMI_DESIGN_ID		0x0000
546a2cdf39Skettenis #define	HDMI_REVISION_ID	0x0001
556a2cdf39Skettenis #define	HDMI_CONFIG0_ID		0x0004
566a2cdf39Skettenis #define	 HDMI_CONFIG0_ID_AUDI2S			(1 << 4)
576a2cdf39Skettenis #define	HDMI_CONFIG2_ID		0x0006
586a2cdf39Skettenis 
596a2cdf39Skettenis #define	HDMI_IH_I2CM_STAT0	0x0105
606a2cdf39Skettenis #define	 HDMI_IH_I2CM_STAT0_DONE		(1 << 1)
616a2cdf39Skettenis #define	 HDMI_IH_I2CM_STAT0_ERROR		(1 << 0)
626a2cdf39Skettenis #define	HDMI_IH_MUTE		0x01ff
636a2cdf39Skettenis #define	 HDMI_IH_MUTE_WAKEUP_INTERRUPT		(1 << 1)
646a2cdf39Skettenis #define	 HDMI_IH_MUTE_ALL_INTERRUPT		(1 << 0)
656a2cdf39Skettenis 
666a2cdf39Skettenis #define	HDMI_TX_INVID0		0x0200
676a2cdf39Skettenis #define	 HDMI_TX_INVID0_VIDEO_MAPPING		(0x1f << 0)
686a2cdf39Skettenis #define	  HDMI_TX_INVID0_VIDEO_MAPPING_DEFAULT	(1 << 0)
696a2cdf39Skettenis #define	HDMI_TX_INSTUFFING	0x0201
706a2cdf39Skettenis #define	 HDMI_TX_INSTUFFING_BCBDATA_STUFFING	(1 << 2)
716a2cdf39Skettenis #define	 HDMI_TX_INSTUFFING_RCRDATA_STUFFING	(1 << 1)
726a2cdf39Skettenis #define	 HDMI_TX_INSTUFFING_GYDATA_STUFFING	(1 << 0)
736a2cdf39Skettenis #define	HDMI_TX_GYDATA0		0x0202
746a2cdf39Skettenis #define	HDMI_TX_GYDATA1		0x0203
756a2cdf39Skettenis #define	HDMI_TX_RCRDATA0	0x0204
766a2cdf39Skettenis #define	HDMI_TX_RCRDATA1	0x0205
776a2cdf39Skettenis #define	HDMI_TX_BCBDATA0	0x0206
786a2cdf39Skettenis #define	HDMI_TX_BCBDATA1	0x0207
796a2cdf39Skettenis 
806a2cdf39Skettenis #define	HDMI_VP_STATUS		0x0800
816a2cdf39Skettenis #define	HDMI_VP_PR_CD		0x0801
826a2cdf39Skettenis #define	 HDMI_VP_PR_CD_COLOR_DEPTH		(0xf << 4)
836a2cdf39Skettenis #define	  HDMI_VP_PR_CD_COLOR_DEPTH_24		0
846a2cdf39Skettenis #define	 HDMI_VP_PR_CD_DESIRED_PR_FACTOR	(0xf << 0)
856a2cdf39Skettenis #define	  HDMI_VP_PR_CD_DESIRED_PR_FACTOR_NONE	0
866a2cdf39Skettenis #define	HDMI_VP_STUFF		0x0802
876a2cdf39Skettenis #define	 HDMI_VP_STUFF_IDEFAULT_PHASE		(1 << 5)
886a2cdf39Skettenis #define	 HDMI_VP_STUFF_YCC422_STUFFING		(1 << 2)
896a2cdf39Skettenis #define	 HDMI_VP_STUFF_PP_STUFFING		(1 << 1)
906a2cdf39Skettenis #define	 HDMI_VP_STUFF_PR_STUFFING		(1 << 0)
916a2cdf39Skettenis #define	HDMI_VP_REMAP		0x0803
926a2cdf39Skettenis #define	 HDMI_VP_REMAP_YCC422_SIZE		(0x3 << 0)
936a2cdf39Skettenis #define	  HDMI_VP_REMAP_YCC422_SIZE_16		0
946a2cdf39Skettenis #define	HDMI_VP_CONF		0x0804
956a2cdf39Skettenis #define	 HDMI_VP_CONF_BYPASS_EN			(1 << 6)
966a2cdf39Skettenis #define	 HDMI_VP_CONF_BYPASS_SELECT		(1 << 2)
976a2cdf39Skettenis #define	 HDMI_VP_CONF_OUTPUT_SELECT		(0x3 << 0)
986a2cdf39Skettenis #define	  HDMI_VP_CONF_OUTPUT_SELECT_BYPASS	(2 << 0)
996a2cdf39Skettenis #define	HDMI_VP_STAT		0x0805
1006a2cdf39Skettenis #define	HDMI_VP_INT		0x0806
1016a2cdf39Skettenis #define	HDMI_VP_MASK		0x0807
1026a2cdf39Skettenis #define	HDMI_VP_POL		0x0808
1036a2cdf39Skettenis 
1046a2cdf39Skettenis #define	HDMI_FC_INVIDCONF	0x1000
1056a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY	(1 << 6)
1066a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY	(1 << 5)
1076a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_DE_IN_POLARITY	(1 << 4)
1086a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_DVI_MODE		(1 << 3)
1096a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC	(1 << 1)
1106a2cdf39Skettenis #define	 HDMI_FC_INVIDCONF_IN_I_P		(1 << 0)
1116a2cdf39Skettenis #define	HDMI_FC_INHACTIV0	0x1001
1126a2cdf39Skettenis #define	HDMI_FC_INHACTIV1	0x1002
1136a2cdf39Skettenis #define	HDMI_FC_INHBLANK0	0x1003
1146a2cdf39Skettenis #define	HDMI_FC_INHBLANK1	0x1004
1156a2cdf39Skettenis #define	HDMI_FC_INVACTIV0	0x1005
1166a2cdf39Skettenis #define	HDMI_FC_INVACTIV1	0x1006
1176a2cdf39Skettenis #define	HDMI_FC_INVBLANK	0x1007
1186a2cdf39Skettenis #define	HDMI_FC_HSYNCINDELAY0	0x1008
1196a2cdf39Skettenis #define	HDMI_FC_HSYNCINDELAY1	0x1009
1206a2cdf39Skettenis #define	HDMI_FC_HSYNCINWIDTH0	0x100a
1216a2cdf39Skettenis #define	HDMI_FC_HSYNCINWIDTH1	0x100b
1226a2cdf39Skettenis #define	HDMI_FC_VSYNCINDELAY	0x100c
1236a2cdf39Skettenis #define	HDMI_FC_VSYNCINWIDTH	0x100d
1246a2cdf39Skettenis #define	HDMI_FC_CTRLDUR		0x1011
1256a2cdf39Skettenis #define	 HDMI_FC_CTRLDUR_DEFAULT		12
1266a2cdf39Skettenis #define	HDMI_FC_EXCTRLDUR	0x1012
1276a2cdf39Skettenis #define	 HDMI_FC_EXCTRLDUR_DEFAULT		32
1286a2cdf39Skettenis #define	HDMI_FC_EXCTRLSPAC	0x1013
1296a2cdf39Skettenis #define	 HDMI_FC_EXCTRLSPAC_DEFAULT		1
1306a2cdf39Skettenis #define	HDMI_FC_CH0PREAM	0x1014
1316a2cdf39Skettenis #define	 HDMI_FC_CH0PREAM_DEFAULT		0x0b
1326a2cdf39Skettenis #define	HDMI_FC_CH1PREAM	0x1015
1336a2cdf39Skettenis #define	 HDMI_FC_CH1PREAM_DEFAULT		0x16
1346a2cdf39Skettenis #define	HDMI_FC_CH2PREAM	0x1016
1356a2cdf39Skettenis #define	 HDMI_FC_CH2PREAM_DEFAULT		0x21
1366a2cdf39Skettenis #define	HDMI_FC_AUDCONF0	0x1025
1376a2cdf39Skettenis #define	HDMI_FC_AUDCONF1	0x1026
1386a2cdf39Skettenis #define	HDMI_FC_AUDCONF2	0x1027
1396a2cdf39Skettenis #define	HDMI_FC_AUDCONF3	0x1028
1406a2cdf39Skettenis 
1416a2cdf39Skettenis #define	HDMI_PHY_CONF0		0x3000
1426a2cdf39Skettenis #define	 HDMI_PHY_CONF0_PDZ			(1 << 7)
1436a2cdf39Skettenis #define	 HDMI_PHY_CONF0_ENTMDS			(1 << 6)
1446a2cdf39Skettenis #define	 HDMI_PHY_CONF0_SVSRET			(1 << 5)
1456a2cdf39Skettenis #define	 HDMI_PHY_CONF0_PDDQ			(1 << 4)
1466a2cdf39Skettenis #define	 HDMI_PHY_CONF0_TXPWRON			(1 << 3)
1476a2cdf39Skettenis #define	 HDMI_PHY_CONF0_ENHPDRXSENSE		(1 << 2)
1486a2cdf39Skettenis #define	 HDMI_PHY_CONF0_SELDATAENPOL		(1 << 1)
1496a2cdf39Skettenis #define	 HDMI_PHY_CONF0_SELDIPIF		(1 << 0)
1506a2cdf39Skettenis #define	HDMI_PHY_STAT0		0x3004
1516a2cdf39Skettenis #define	 HDMI_PHY_STAT0_RX_SENSE_3		(1 << 7)
1526a2cdf39Skettenis #define	 HDMI_PHY_STAT0_RX_SENSE_2		(1 << 6)
1536a2cdf39Skettenis #define	 HDMI_PHY_STAT0_RX_SENSE_1		(1 << 5)
1546a2cdf39Skettenis #define	 HDMI_PHY_STAT0_RX_SENSE_0		(1 << 4)
1556a2cdf39Skettenis #define	 HDMI_PHY_STAT0_HPD			(1 << 1)
1566a2cdf39Skettenis #define	 HDMI_PHY_STAT0_TX_PHY_LOCK		(1 << 0)
1576a2cdf39Skettenis 
1586a2cdf39Skettenis #define	HDMI_AUD_CONF0		0x3100
1596a2cdf39Skettenis #define	 HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST	(1 << 7)
1606a2cdf39Skettenis #define	 HDMI_AUD_CONF0_I2S_SELECT		(1 << 5)
1616a2cdf39Skettenis #define	 HDMI_AUD_CONF0_I2S_IN_EN		(0xf << 0)
1626a2cdf39Skettenis #define	HDMI_AUD_CONF1		0x3101
1636a2cdf39Skettenis #define	 HDMI_AUD_CONF1_I2S_WIDTH		(0x1f << 0)
1646a2cdf39Skettenis #define	HDMI_AUD_INT		0x3102
1656a2cdf39Skettenis #define	HDMI_AUD_CONF2		0x3103
1666a2cdf39Skettenis #define	 HDMI_AUD_CONF2_INSERT_PCUV		(1 << 2)
1676a2cdf39Skettenis #define	 HDMI_AUD_CONF2_NLPCM			(1 << 1)
1686a2cdf39Skettenis #define	 HDMI_AUD_CONF2_HBR			(1 << 0)
1696a2cdf39Skettenis #define	HDMI_AUD_INT1		0x3104
1706a2cdf39Skettenis 
1716a2cdf39Skettenis #define	HDMI_AUD_N1		0x3200
1726a2cdf39Skettenis #define	HDMI_AUD_N2		0x3201
1736a2cdf39Skettenis #define	HDMI_AUD_N3		0x3202
1746a2cdf39Skettenis #define	HDMI_AUD_CTS1		0x3203
1756a2cdf39Skettenis #define	HDMI_AUD_CTS2		0x3204
1766a2cdf39Skettenis #define	HDMI_AUD_CTS3		0x3205
1776a2cdf39Skettenis #define	HDMI_AUD_INPUTCLKFS	0x3206
1786a2cdf39Skettenis #define	 HDMI_AUD_INPUTCLKFS_IFSFACTOR		(0x7 << 0)
1796a2cdf39Skettenis 
1806a2cdf39Skettenis #define	HDMI_MC_CLKDIS		0x4001
1816a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_HDCPCLK_DISABLE		(1 << 6)
1826a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_CECCLK_DISABLE		(1 << 5)
1836a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_CSCCLK_DISABLE		(1 << 4)
1846a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_AUDCLK_DISABLE		(1 << 3)
1856a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_PREPCLK_DISABLE		(1 << 2)
1866a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_TMDSCLK_DISABLE		(1 << 1)
1876a2cdf39Skettenis #define	 HDMI_MC_CLKDIS_PIXELCLK_DISABLE	(1 << 0)
1886a2cdf39Skettenis #define	HDMI_MC_SWRSTZREQ	0x4002
1896a2cdf39Skettenis #define	 HDMI_MC_SWRSTZREQ_CECSWRST_REQ		__BIT(6)
1906a2cdf39Skettenis #define	 HDMI_MC_SWRSTZREQ_PREPSWRST_REQ	(1 << 2)
1916a2cdf39Skettenis #define	 HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ	(1 << 1)
1926a2cdf39Skettenis #define	 HDMI_MC_SWRSTZREQ_PIXELSWRST_REQ	(1 << 0)
1936a2cdf39Skettenis #define	HDMI_MC_FLOWCTRL	0x4004
1946a2cdf39Skettenis #define	HDMI_MC_PHYRSTZ		0x4005
1956a2cdf39Skettenis #define	 HDMI_MC_PHYRSTZ_ASSERT			(1 << 0)
1966a2cdf39Skettenis #define	 HDMI_MC_PHYRSTZ_DEASSERT		0
1976a2cdf39Skettenis #define	HDMI_MC_LOCKONCLOCK	0x4006
1986a2cdf39Skettenis #define	HDMI_MC_HEACPHY_RST	0x4007
1996a2cdf39Skettenis 
2006a2cdf39Skettenis #define	HDMI_I2CM_SLAVE		0x7e00
2016a2cdf39Skettenis #define	HDMI_I2CM_ADDRESS	0x7e01
2026a2cdf39Skettenis #define	HDMI_I2CM_DATAO		0x7e02
2036a2cdf39Skettenis #define	HDMI_I2CM_DATAI		0x7e03
2046a2cdf39Skettenis #define	HDMI_I2CM_OPERATION	0x7e04
2056a2cdf39Skettenis #define	 HDMI_I2CM_OPERATION_WR			(1 << 4)
2066a2cdf39Skettenis #define	 HDMI_I2CM_OPERATION_RD_EXT		(1 << 1)
2076a2cdf39Skettenis #define	 HDMI_I2CM_OPERATION_RD			(1 << 0)
2086a2cdf39Skettenis #define	HDMI_I2CM_INT		0x7e05
2096a2cdf39Skettenis #define	 HDMI_I2CM_INT_DONE_POL			(1 << 3)
2106a2cdf39Skettenis #define	 HDMI_I2CM_INT_DONE_MASK		(1 << 2)
2116a2cdf39Skettenis #define	 HDMI_I2CM_INT_DONE_INTERRUPT		(1 << 1)
2126a2cdf39Skettenis #define	 HDMI_I2CM_INT_DONE_STATUS		(1 << 0)
2136a2cdf39Skettenis #define	 HDMI_I2CM_INT_DEFAULT			\
2146a2cdf39Skettenis 	(HDMI_I2CM_INT_DONE_POL|		\
2156a2cdf39Skettenis 	 HDMI_I2CM_INT_DONE_INTERRUPT|		\
2166a2cdf39Skettenis 	 HDMI_I2CM_INT_DONE_STATUS)
2176a2cdf39Skettenis #define	HDMI_I2CM_CTLINT	0x7e06
2186a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_NACK_POL		(1 << 7)
2196a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_NACK_MASK		(1 << 6)
2206a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_NACK_INTERRUPT	(1 << 5)
2216a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_NACK_STATUS		(1 << 4)
2226a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_ARB_POL		(1 << 3)
2236a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_ARB_MASK		(1 << 2)
2246a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_ARB_INTERRUPT		(1 << 1)
2256a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_ARB_STATUS		(1 << 0)
2266a2cdf39Skettenis #define	 HDMI_I2CM_CTLINT_DEFAULT		\
2276a2cdf39Skettenis 	(HDMI_I2CM_CTLINT_NACK_POL|		\
2286a2cdf39Skettenis 	 HDMI_I2CM_CTLINT_NACK_INTERRUPT|	\
2296a2cdf39Skettenis 	 HDMI_I2CM_CTLINT_NACK_STATUS|		\
2306a2cdf39Skettenis 	 HDMI_I2CM_CTLINT_ARB_POL|		\
2316a2cdf39Skettenis 	 HDMI_I2CM_CTLINT_ARB_INTERRUPT|	\
2326a2cdf39Skettenis 	 HDMI_I2CM_CTLINT_ARB_STATUS)
2336a2cdf39Skettenis #define	HDMI_I2CM_DIV		0x7e07
2346a2cdf39Skettenis #define	 HDMI_I2CM_DIV_FAST_STD_MODE		(1 << 3)
2356a2cdf39Skettenis #define	HDMI_I2CM_SEGADDR	0x7e08
2366a2cdf39Skettenis #define	 HDMI_I2CM_SEGADDR_SEGADDR		(0x7f << 0)
2376a2cdf39Skettenis #define	HDMI_I2CM_SOFTRSTZ	0x7e09
2386a2cdf39Skettenis #define	 HDMI_I2CM_SOFTRSTZ_I2C_SOFTRST		(1 << 0)
2396a2cdf39Skettenis #define	HDMI_I2CM_SEGPTR	0x7e0a
2406a2cdf39Skettenis #define	HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x730c
2416a2cdf39Skettenis #define	HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x730e
2426a2cdf39Skettenis 
2436a2cdf39Skettenis enum dwhdmi_dai_mixer_ctrl {
2446a2cdf39Skettenis 	DWHDMI_DAI_OUTPUT_CLASS,
2456a2cdf39Skettenis 	DWHDMI_DAI_INPUT_CLASS,
2466a2cdf39Skettenis 
2476a2cdf39Skettenis 	DWHDMI_DAI_OUTPUT_MASTER_VOLUME,
2486a2cdf39Skettenis 	DWHDMI_DAI_INPUT_DAC_VOLUME,
2496a2cdf39Skettenis 
2506a2cdf39Skettenis 	DWHDMI_DAI_MIXER_CTRL_LAST
2516a2cdf39Skettenis };
2526a2cdf39Skettenis 
2536a2cdf39Skettenis int
dwhdmi_ddc_exec(void * priv,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)2546a2cdf39Skettenis dwhdmi_ddc_exec(void *priv, i2c_op_t op, i2c_addr_t addr,
2556a2cdf39Skettenis     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
2566a2cdf39Skettenis {
2576a2cdf39Skettenis 	struct dwhdmi_softc * const sc = priv;
2586a2cdf39Skettenis 	uint8_t block, operation, val;
2596a2cdf39Skettenis 	uint8_t *pbuf = buf;
2606a2cdf39Skettenis 	int off, n, retry;
2616a2cdf39Skettenis 
2626a2cdf39Skettenis 	if (addr != DDC_ADDR || op != I2C_OP_READ_WITH_STOP || cmdlen == 0 || buf == NULL) {
2636a2cdf39Skettenis 		printf("%s: bad args addr=%#x op=%#x cmdlen=%d buf=%p\n",
2646a2cdf39Skettenis 		    __func__, addr, op, (int)cmdlen, buf);
2656a2cdf39Skettenis 		return ENXIO;
2666a2cdf39Skettenis 	}
2676a2cdf39Skettenis 	if (len > 256) {
2686a2cdf39Skettenis 		printf("dwhdmi_ddc_exec: bad len %d\n", (int)len);
2696a2cdf39Skettenis 		return ERANGE;
2706a2cdf39Skettenis 	}
2716a2cdf39Skettenis 
2726a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_I2CM_SOFTRSTZ, 0);
2736a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_IH_I2CM_STAT0, dwhdmi_read(sc, HDMI_IH_I2CM_STAT0));
2746a2cdf39Skettenis 	if (sc->sc_scl_hcnt)
2756a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_I2CM_SS_SCL_HCNT_0_ADDR, sc->sc_scl_hcnt);
2766a2cdf39Skettenis 	if (sc->sc_scl_lcnt)
2776a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_I2CM_SS_SCL_LCNT_0_ADDR, sc->sc_scl_lcnt);
2786a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_I2CM_DIV, 0);
2796a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_I2CM_SLAVE, DDC_ADDR);
2806a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_I2CM_SEGADDR, DDC_SEGMENT_ADDR);
2816a2cdf39Skettenis 
2826a2cdf39Skettenis 	block = *(const uint8_t *)cmdbuf;
2836a2cdf39Skettenis 	operation = block ? HDMI_I2CM_OPERATION_RD_EXT : HDMI_I2CM_OPERATION_RD;
2846a2cdf39Skettenis 	off = (block & 1) ? 128 : 0;
2856a2cdf39Skettenis 
2866a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_I2CM_SEGPTR, block >> 1);
2876a2cdf39Skettenis 
2886a2cdf39Skettenis 	for (n = 0; n < len; n++) {
2896a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_I2CM_ADDRESS, n + off);
2906a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_I2CM_OPERATION, operation);
2916a2cdf39Skettenis 		for (retry = 10000; retry > 0; retry--) {
2926a2cdf39Skettenis 			val = dwhdmi_read(sc, HDMI_IH_I2CM_STAT0);
2936a2cdf39Skettenis 			if (val & HDMI_IH_I2CM_STAT0_ERROR) {
2946a2cdf39Skettenis 				return EIO;
2956a2cdf39Skettenis 			}
2966a2cdf39Skettenis 			if (val & HDMI_IH_I2CM_STAT0_DONE) {
2976a2cdf39Skettenis 				dwhdmi_write(sc, HDMI_IH_I2CM_STAT0, val);
2986a2cdf39Skettenis 				break;
2996a2cdf39Skettenis 			}
3006a2cdf39Skettenis 			delay(1);
3016a2cdf39Skettenis 		}
3026a2cdf39Skettenis 		if (retry == 0) {
3036a2cdf39Skettenis 			printf("dwhdmi_ddc_exec: timeout waiting for xfer, stat0=%#x\n", dwhdmi_read(sc, HDMI_IH_I2CM_STAT0));
3046a2cdf39Skettenis 			return ETIMEDOUT;
3056a2cdf39Skettenis 		}
3066a2cdf39Skettenis 
3076a2cdf39Skettenis 		pbuf[n] = dwhdmi_read(sc, HDMI_I2CM_DATAI);
3086a2cdf39Skettenis 	}
3096a2cdf39Skettenis 
3106a2cdf39Skettenis 	return 0;
3116a2cdf39Skettenis }
3126a2cdf39Skettenis 
3136a2cdf39Skettenis uint8_t
dwhdmi_read(struct dwhdmi_softc * sc,bus_size_t reg)3146a2cdf39Skettenis dwhdmi_read(struct dwhdmi_softc *sc, bus_size_t reg)
3156a2cdf39Skettenis {
3166a2cdf39Skettenis 	uint8_t val;
3176a2cdf39Skettenis 
3186a2cdf39Skettenis 	switch (sc->sc_reg_width) {
3196a2cdf39Skettenis 	case 1:
3206a2cdf39Skettenis 		val = bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg);
3216a2cdf39Skettenis 		break;
3226a2cdf39Skettenis 	case 4:
3236a2cdf39Skettenis 		val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg * 4) & 0xff;
3246a2cdf39Skettenis 		break;
3256a2cdf39Skettenis 	default:
3266a2cdf39Skettenis 		val = 0;
3276a2cdf39Skettenis 		break;
3286a2cdf39Skettenis 	}
3296a2cdf39Skettenis 
3306a2cdf39Skettenis 	return val;
3316a2cdf39Skettenis }
3326a2cdf39Skettenis 
3336a2cdf39Skettenis void
dwhdmi_write(struct dwhdmi_softc * sc,bus_size_t reg,uint8_t val)3346a2cdf39Skettenis dwhdmi_write(struct dwhdmi_softc *sc, bus_size_t reg, uint8_t val)
3356a2cdf39Skettenis {
3366a2cdf39Skettenis 	switch (sc->sc_reg_width) {
3376a2cdf39Skettenis 	case 1:
3386a2cdf39Skettenis 		bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val);
3396a2cdf39Skettenis 		break;
3406a2cdf39Skettenis 	case 4:
3416a2cdf39Skettenis 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg * 4, val);
3426a2cdf39Skettenis 		break;
3436a2cdf39Skettenis 	}
3446a2cdf39Skettenis }
3456a2cdf39Skettenis 
3466a2cdf39Skettenis void
dwhdmi_vp_init(struct dwhdmi_softc * sc)3476a2cdf39Skettenis dwhdmi_vp_init(struct dwhdmi_softc *sc)
3486a2cdf39Skettenis {
3496a2cdf39Skettenis 	uint8_t val;
3506a2cdf39Skettenis 
3516a2cdf39Skettenis 	/* Select 24-bits per pixel video, 8-bit packing mode and disable pixel repetition */
3526a2cdf39Skettenis 	val = HDMI_VP_PR_CD_COLOR_DEPTH_24 << 4 |
3536a2cdf39Skettenis 	      HDMI_VP_PR_CD_DESIRED_PR_FACTOR_NONE << 0;
3546a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_VP_PR_CD, val);
3556a2cdf39Skettenis 
3566a2cdf39Skettenis 	/* Configure stuffing */
3576a2cdf39Skettenis 	val = HDMI_VP_STUFF_IDEFAULT_PHASE |
3586a2cdf39Skettenis 	      HDMI_VP_STUFF_YCC422_STUFFING |
3596a2cdf39Skettenis 	      HDMI_VP_STUFF_PP_STUFFING |
3606a2cdf39Skettenis 	      HDMI_VP_STUFF_PR_STUFFING;
3616a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_VP_STUFF, val);
3626a2cdf39Skettenis 
3636a2cdf39Skettenis 	/* Set YCC422 remap to 16-bit input video */
3646a2cdf39Skettenis 	val = HDMI_VP_REMAP_YCC422_SIZE_16 << 0;
3656a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_VP_REMAP, val);
3666a2cdf39Skettenis 
3676a2cdf39Skettenis 	/* Configure video packetizer */
3686a2cdf39Skettenis 	val = HDMI_VP_CONF_BYPASS_EN |
3696a2cdf39Skettenis 	      HDMI_VP_CONF_BYPASS_SELECT |
3706a2cdf39Skettenis 	      HDMI_VP_CONF_OUTPUT_SELECT_BYPASS;
3716a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_VP_CONF, val);
3726a2cdf39Skettenis }
3736a2cdf39Skettenis 
3746a2cdf39Skettenis void
dwhdmi_tx_init(struct dwhdmi_softc * sc)3756a2cdf39Skettenis dwhdmi_tx_init(struct dwhdmi_softc *sc)
3766a2cdf39Skettenis {
3776a2cdf39Skettenis 	uint8_t val;
3786a2cdf39Skettenis 
3796a2cdf39Skettenis 	/* Disable internal data enable generator and set default video mapping */
3806a2cdf39Skettenis 	val = HDMI_TX_INVID0_VIDEO_MAPPING_DEFAULT;
3816a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_TX_INVID0, val);
3826a2cdf39Skettenis 
3836a2cdf39Skettenis 	/* Enable video sampler stuffing */
3846a2cdf39Skettenis 	val = HDMI_TX_INSTUFFING_BCBDATA_STUFFING |
3856a2cdf39Skettenis 	      HDMI_TX_INSTUFFING_RCRDATA_STUFFING |
3866a2cdf39Skettenis 	      HDMI_TX_INSTUFFING_GYDATA_STUFFING;
3876a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_TX_INSTUFFING, val);
3886a2cdf39Skettenis }
3896a2cdf39Skettenis 
3906a2cdf39Skettenis int
dwhdmi_cea_mode_uses_fractional_vblank(uint8_t vic)3916a2cdf39Skettenis dwhdmi_cea_mode_uses_fractional_vblank(uint8_t vic)
3926a2cdf39Skettenis {
3936a2cdf39Skettenis 	const uint8_t match[] = { 5, 6, 7, 10, 11, 20, 21, 22 };
3946a2cdf39Skettenis 	u_int n;
3956a2cdf39Skettenis 
3966a2cdf39Skettenis 	for (n = 0; n < nitems(match); n++)
3976a2cdf39Skettenis 		if (match[n] == vic)
3986a2cdf39Skettenis 			return true;
3996a2cdf39Skettenis 
4006a2cdf39Skettenis 	return false;
4016a2cdf39Skettenis }
4026a2cdf39Skettenis 
4036a2cdf39Skettenis void
dwhdmi_fc_init(struct dwhdmi_softc * sc,struct drm_display_mode * mode)4046a2cdf39Skettenis dwhdmi_fc_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
4056a2cdf39Skettenis {
4066a2cdf39Skettenis 	struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector;
4076a2cdf39Skettenis 	uint8_t val;
4086a2cdf39Skettenis 
4096a2cdf39Skettenis 	const uint8_t vic = drm_match_cea_mode(mode);
4106a2cdf39Skettenis 	const uint16_t inhactiv = mode->crtc_hdisplay;
4116a2cdf39Skettenis 	const uint16_t inhblank = mode->crtc_htotal - mode->crtc_hdisplay;
4126a2cdf39Skettenis 	const uint16_t invactiv = mode->crtc_vdisplay;
4136a2cdf39Skettenis 	const uint8_t invblank = mode->crtc_vtotal - mode->crtc_vdisplay;
4146a2cdf39Skettenis 	const uint16_t hsyncindelay = mode->crtc_hsync_start - mode->crtc_hdisplay;
4156a2cdf39Skettenis 	const uint16_t hsyncinwidth = mode->crtc_hsync_end - mode->crtc_hsync_start;
4166a2cdf39Skettenis 	const uint8_t vsyncindelay = mode->crtc_vsync_start - mode->crtc_vdisplay;
4176a2cdf39Skettenis 	const uint8_t vsyncinwidth = mode->crtc_vsync_end - mode->crtc_vsync_start;
4186a2cdf39Skettenis 
4196a2cdf39Skettenis 	/* Input video configuration for frame composer */
4206a2cdf39Skettenis 	val = HDMI_FC_INVIDCONF_DE_IN_POLARITY;
4216a2cdf39Skettenis 	if ((mode->flags & DRM_MODE_FLAG_PVSYNC) != 0)
4226a2cdf39Skettenis 		val |= HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY;
4236a2cdf39Skettenis 	if ((mode->flags & DRM_MODE_FLAG_PHSYNC) != 0)
4246a2cdf39Skettenis 		val |= HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY;
4256a2cdf39Skettenis 	if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != 0)
4266a2cdf39Skettenis 		val |= HDMI_FC_INVIDCONF_IN_I_P;
4276a2cdf39Skettenis 	if (dwhdmi_connector->hdmi_monitor)
4286a2cdf39Skettenis 		val |= HDMI_FC_INVIDCONF_DVI_MODE;
4296a2cdf39Skettenis 	if (dwhdmi_cea_mode_uses_fractional_vblank(vic))
4306a2cdf39Skettenis 		val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC;
4316a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INVIDCONF, val);
4326a2cdf39Skettenis 
4336a2cdf39Skettenis 	/* Input video mode timings */
4346a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INHACTIV0, inhactiv & 0xff);
4356a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INHACTIV1, inhactiv >> 8);
4366a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INHBLANK0, inhblank & 0xff);
4376a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INHBLANK1, inhblank >> 8);
4386a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INVACTIV0, invactiv & 0xff);
4396a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INVACTIV1, invactiv >> 8);
4406a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_INVBLANK, invblank);
4416a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_HSYNCINDELAY0, hsyncindelay & 0xff);
4426a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_HSYNCINDELAY1, hsyncindelay >> 8);
4436a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_HSYNCINWIDTH0, hsyncinwidth & 0xff);
4446a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_HSYNCINWIDTH1, hsyncinwidth >> 8);
4456a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_VSYNCINDELAY, vsyncindelay);
4466a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_VSYNCINWIDTH, vsyncinwidth);
4476a2cdf39Skettenis 
4486a2cdf39Skettenis 	/* Setup control period minimum durations */
4496a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_CTRLDUR, HDMI_FC_CTRLDUR_DEFAULT);
4506a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_EXCTRLDUR, HDMI_FC_EXCTRLDUR_DEFAULT);
4516a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_EXCTRLSPAC, HDMI_FC_EXCTRLSPAC_DEFAULT);
4526a2cdf39Skettenis 
4536a2cdf39Skettenis 	/* Setup channel preamble filters */
4546a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_CH0PREAM, HDMI_FC_CH0PREAM_DEFAULT);
4556a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_CH1PREAM, HDMI_FC_CH1PREAM_DEFAULT);
4566a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_CH2PREAM, HDMI_FC_CH2PREAM_DEFAULT);
4576a2cdf39Skettenis }
4586a2cdf39Skettenis 
4596a2cdf39Skettenis void
dwhdmi_mc_init(struct dwhdmi_softc * sc)4606a2cdf39Skettenis dwhdmi_mc_init(struct dwhdmi_softc *sc)
4616a2cdf39Skettenis {
4626a2cdf39Skettenis 	uint8_t val;
4636a2cdf39Skettenis 	u_int n, iter;
4646a2cdf39Skettenis 
4656a2cdf39Skettenis 	/* Bypass colour space converter */
4666a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_MC_FLOWCTRL, 0);
4676a2cdf39Skettenis 
4686a2cdf39Skettenis 	/* Enable TMDS, pixel, and (if required) audio sampler clocks */
4696a2cdf39Skettenis 	val = HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
4706a2cdf39Skettenis 	      HDMI_MC_CLKDIS_CECCLK_DISABLE |
4716a2cdf39Skettenis 	      HDMI_MC_CLKDIS_CSCCLK_DISABLE |
4726a2cdf39Skettenis 	      HDMI_MC_CLKDIS_PREPCLK_DISABLE;
4736a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
4746a2cdf39Skettenis 
4756a2cdf39Skettenis 	/* Soft reset TMDS */
4766a2cdf39Skettenis 	val = 0xff & ~HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ;
4776a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_MC_SWRSTZREQ, val);
4786a2cdf39Skettenis 
4796a2cdf39Skettenis 	iter = sc->sc_version == 0x130a ? 4 : 1;
4806a2cdf39Skettenis 
4816a2cdf39Skettenis 	val = dwhdmi_read(sc, HDMI_FC_INVIDCONF);
4826a2cdf39Skettenis 	for (n = 0; n < iter; n++)
4836a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_FC_INVIDCONF, val);
4846a2cdf39Skettenis }
4856a2cdf39Skettenis 
4866a2cdf39Skettenis void
dwhdmi_mc_disable(struct dwhdmi_softc * sc)4876a2cdf39Skettenis dwhdmi_mc_disable(struct dwhdmi_softc *sc)
4886a2cdf39Skettenis {
4896a2cdf39Skettenis 	/* Disable clocks */
4906a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_MC_CLKDIS, 0xff);
4916a2cdf39Skettenis }
4926a2cdf39Skettenis 
4936a2cdf39Skettenis void
dwhdmi_audio_init(struct dwhdmi_softc * sc)4946a2cdf39Skettenis dwhdmi_audio_init(struct dwhdmi_softc *sc)
4956a2cdf39Skettenis {
4966a2cdf39Skettenis 	uint8_t val;
4976a2cdf39Skettenis 	u_int n;
4986a2cdf39Skettenis 
4996a2cdf39Skettenis 	/* The following values are for 48 kHz */
5006a2cdf39Skettenis 	switch (sc->sc_curmode.clock) {
5016a2cdf39Skettenis 	case 25170:
5026a2cdf39Skettenis 		n = 6864;
5036a2cdf39Skettenis 		break;
5046a2cdf39Skettenis 	case 74170:
5056a2cdf39Skettenis 		n = 11648;
5066a2cdf39Skettenis 		break;
5076a2cdf39Skettenis 	case 148350:
5086a2cdf39Skettenis 		n = 5824;
5096a2cdf39Skettenis 		break;
5106a2cdf39Skettenis 	default:
5116a2cdf39Skettenis 		n = 6144;
5126a2cdf39Skettenis 		break;
5136a2cdf39Skettenis 	}
5146a2cdf39Skettenis 
5156a2cdf39Skettenis 	/* Use automatic CTS generation */
5166a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_CTS1, 0);
5176a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_CTS2, 0);
5186a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_CTS3, 0);
5196a2cdf39Skettenis 
5206a2cdf39Skettenis 	/* Set N factor for audio clock regeneration */
5216a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_N1, n & 0xff);
5226a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
5236a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
5246a2cdf39Skettenis 
5256a2cdf39Skettenis 	val = dwhdmi_read(sc, HDMI_AUD_CONF0);
5266a2cdf39Skettenis 	val |= HDMI_AUD_CONF0_I2S_SELECT;		/* XXX i2s mode */
5276a2cdf39Skettenis 	val &= ~HDMI_AUD_CONF0_I2S_IN_EN;
5286a2cdf39Skettenis 	val |= (1 << 0);					/* XXX 2ch */
5296a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_CONF0, val);
5306a2cdf39Skettenis 
5316a2cdf39Skettenis 	val = (16 << 0);
5326a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_CONF1, val);
5336a2cdf39Skettenis 
5346a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_AUD_INPUTCLKFS, 4);	/* XXX 64 FS */
5356a2cdf39Skettenis 
5366a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_AUDCONF0, 1 << 4);	/* XXX 2ch */
5376a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_AUDCONF1, 0);
5386a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_AUDCONF2, 0);
5396a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_FC_AUDCONF3, 0);
5406a2cdf39Skettenis 
5416a2cdf39Skettenis 	val = dwhdmi_read(sc, HDMI_MC_CLKDIS);
5426a2cdf39Skettenis 	val &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
5436a2cdf39Skettenis 	dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
5446a2cdf39Skettenis }
5456a2cdf39Skettenis 
5466a2cdf39Skettenis enum drm_connector_status
dwhdmi_connector_detect(struct drm_connector * connector,bool force)5476a2cdf39Skettenis dwhdmi_connector_detect(struct drm_connector *connector, bool force)
5486a2cdf39Skettenis {
5496a2cdf39Skettenis 	struct dwhdmi_connector *dwhdmi_connector = to_dwhdmi_connector(connector);
5506a2cdf39Skettenis 	struct dwhdmi_softc * const sc = dwhdmi_connector->sc;
5516a2cdf39Skettenis 
5526a2cdf39Skettenis 	if (sc->sc_detect != NULL)
5536a2cdf39Skettenis 		return sc->sc_detect(sc, force);
5546a2cdf39Skettenis 
5556a2cdf39Skettenis 	return connector_status_connected;
5566a2cdf39Skettenis }
5576a2cdf39Skettenis 
5586a2cdf39Skettenis void
dwhdmi_connector_destroy(struct drm_connector * connector)5596a2cdf39Skettenis dwhdmi_connector_destroy(struct drm_connector *connector)
5606a2cdf39Skettenis {
5616a2cdf39Skettenis 	drm_connector_unregister(connector);
5626a2cdf39Skettenis 	drm_connector_cleanup(connector);
5636a2cdf39Skettenis }
5646a2cdf39Skettenis 
5656a2cdf39Skettenis const struct drm_connector_funcs dwhdmi_connector_funcs = {
5666a2cdf39Skettenis 	.dpms = drm_helper_connector_dpms,
5676a2cdf39Skettenis 	.detect = dwhdmi_connector_detect,
5686a2cdf39Skettenis 	.fill_modes = drm_helper_probe_single_connector_modes,
5696a2cdf39Skettenis 	.destroy = dwhdmi_connector_destroy,
570c349dbc7Sjsg 	.reset = drm_atomic_helper_connector_reset,
571c349dbc7Sjsg 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
572c349dbc7Sjsg 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
5736a2cdf39Skettenis };
5746a2cdf39Skettenis 
5756a2cdf39Skettenis int
dwhdmi_connector_get_modes(struct drm_connector * connector)5766a2cdf39Skettenis dwhdmi_connector_get_modes(struct drm_connector *connector)
5776a2cdf39Skettenis {
5786a2cdf39Skettenis 	struct dwhdmi_connector *dwhdmi_connector = to_dwhdmi_connector(connector);
5796a2cdf39Skettenis 	struct dwhdmi_softc * const sc = dwhdmi_connector->sc;
5806a2cdf39Skettenis 	struct i2c_adapter ddc;
5816a2cdf39Skettenis 	struct edid *edid;
5826a2cdf39Skettenis 	int error = 0;
5836a2cdf39Skettenis 
5846a2cdf39Skettenis 	memset(&ddc, 0, sizeof(ddc));
5856a2cdf39Skettenis 	ddc.ic = *sc->sc_ic;
5866a2cdf39Skettenis 
5876a2cdf39Skettenis 	edid = drm_get_edid(connector, &ddc);
5886a2cdf39Skettenis 	if (edid) {
5896a2cdf39Skettenis 		dwhdmi_connector->hdmi_monitor = drm_detect_hdmi_monitor(edid);
5906a2cdf39Skettenis 		dwhdmi_connector->monitor_audio = drm_detect_monitor_audio(edid);
5916a2cdf39Skettenis 		drm_connector_update_edid_property(connector, edid);
5926a2cdf39Skettenis 		error = drm_add_edid_modes(connector, edid);
5936a2cdf39Skettenis 		kfree(edid);
5946a2cdf39Skettenis 	} else {
5956a2cdf39Skettenis 		dwhdmi_connector->hdmi_monitor = false;
5966a2cdf39Skettenis 		dwhdmi_connector->monitor_audio = false;
5976a2cdf39Skettenis 	}
5986a2cdf39Skettenis 
5996a2cdf39Skettenis 	return error;
6006a2cdf39Skettenis }
6016a2cdf39Skettenis 
6026a2cdf39Skettenis const struct drm_connector_helper_funcs dwhdmi_connector_helper_funcs = {
6036a2cdf39Skettenis 	.get_modes = dwhdmi_connector_get_modes,
6046a2cdf39Skettenis };
6056a2cdf39Skettenis 
6066a2cdf39Skettenis int
dwhdmi_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)607c349dbc7Sjsg dwhdmi_bridge_attach(struct drm_bridge *bridge,
608c349dbc7Sjsg     enum drm_bridge_attach_flags flags)
6096a2cdf39Skettenis {
6106a2cdf39Skettenis 	struct dwhdmi_softc * const sc = bridge->driver_private;
6116a2cdf39Skettenis 	struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector;
6126a2cdf39Skettenis 	struct drm_connector *connector = &dwhdmi_connector->base;
6136a2cdf39Skettenis 	int error;
6146a2cdf39Skettenis 
6156a2cdf39Skettenis 	dwhdmi_connector->sc = sc;
6166a2cdf39Skettenis 
6176a2cdf39Skettenis 	connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
6186a2cdf39Skettenis 	connector->interlace_allowed = 0;
6196a2cdf39Skettenis 	connector->doublescan_allowed = 0;
6206a2cdf39Skettenis 
6216a2cdf39Skettenis 	drm_connector_init(bridge->dev, connector, &dwhdmi_connector_funcs,
6226a2cdf39Skettenis 	    DRM_MODE_CONNECTOR_HDMIA);
6236a2cdf39Skettenis 	drm_connector_helper_add(connector, &dwhdmi_connector_helper_funcs);
6246a2cdf39Skettenis 
6256a2cdf39Skettenis 	error = drm_connector_attach_encoder(connector, bridge->encoder);
6266a2cdf39Skettenis 	if (error != 0)
6276a2cdf39Skettenis 		return error;
6286a2cdf39Skettenis 
6296a2cdf39Skettenis 	return drm_connector_register(connector);
6306a2cdf39Skettenis }
6316a2cdf39Skettenis 
6326a2cdf39Skettenis void
dwhdmi_bridge_enable(struct drm_bridge * bridge)6336a2cdf39Skettenis dwhdmi_bridge_enable(struct drm_bridge *bridge)
6346a2cdf39Skettenis {
6356a2cdf39Skettenis 	struct dwhdmi_softc * const sc = bridge->driver_private;
6366a2cdf39Skettenis 
6376a2cdf39Skettenis 	dwhdmi_vp_init(sc);
6386a2cdf39Skettenis 	dwhdmi_fc_init(sc, &sc->sc_curmode);
6396a2cdf39Skettenis 
6406a2cdf39Skettenis 	if (sc->sc_enable)
6416a2cdf39Skettenis 		sc->sc_enable(sc);
6426a2cdf39Skettenis 
6436a2cdf39Skettenis 	dwhdmi_tx_init(sc);
6446a2cdf39Skettenis 	dwhdmi_mc_init(sc);
6456a2cdf39Skettenis 
6466a2cdf39Skettenis 	if (sc->sc_connector.monitor_audio)
6476a2cdf39Skettenis 		dwhdmi_audio_init(sc);
6486a2cdf39Skettenis }
6496a2cdf39Skettenis 
6506a2cdf39Skettenis void
dwhdmi_bridge_pre_enable(struct drm_bridge * bridge)6516a2cdf39Skettenis dwhdmi_bridge_pre_enable(struct drm_bridge *bridge)
6526a2cdf39Skettenis {
6536a2cdf39Skettenis }
6546a2cdf39Skettenis 
6556a2cdf39Skettenis void
dwhdmi_bridge_disable(struct drm_bridge * bridge)6566a2cdf39Skettenis dwhdmi_bridge_disable(struct drm_bridge *bridge)
6576a2cdf39Skettenis {
6586a2cdf39Skettenis 	struct dwhdmi_softc * const sc = bridge->driver_private;
6596a2cdf39Skettenis 
6606a2cdf39Skettenis 	if (sc->sc_disable)
6616a2cdf39Skettenis 		sc->sc_disable(sc);
6626a2cdf39Skettenis 
6636a2cdf39Skettenis 	dwhdmi_mc_disable(sc);
6646a2cdf39Skettenis }
6656a2cdf39Skettenis 
6666a2cdf39Skettenis void
dwhdmi_bridge_post_disable(struct drm_bridge * bridge)6676a2cdf39Skettenis dwhdmi_bridge_post_disable(struct drm_bridge *bridge)
6686a2cdf39Skettenis {
6696a2cdf39Skettenis }
6706a2cdf39Skettenis 
6716a2cdf39Skettenis void
dwhdmi_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)6726a2cdf39Skettenis dwhdmi_bridge_mode_set(struct drm_bridge *bridge,
673c349dbc7Sjsg     const struct drm_display_mode *mode,
674c349dbc7Sjsg     const struct drm_display_mode *adjusted_mode)
6756a2cdf39Skettenis {
67613a9a14fSkettenis 	struct dwhdmi_softc *sc = bridge->driver_private;
6776a2cdf39Skettenis 
6786a2cdf39Skettenis 	if (sc->sc_mode_set)
6796a2cdf39Skettenis 		sc->sc_mode_set(sc, mode, adjusted_mode);
6806a2cdf39Skettenis 
6816a2cdf39Skettenis 	sc->sc_curmode = *adjusted_mode;
6826a2cdf39Skettenis }
6836a2cdf39Skettenis 
68413a9a14fSkettenis enum drm_mode_status
dwhdmi_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)68513a9a14fSkettenis dwhdmi_bridge_mode_valid(struct drm_bridge *bridge,
686ad8b1aafSjsg     const struct drm_display_info *info,
68713a9a14fSkettenis     const struct drm_display_mode *mode)
6886a2cdf39Skettenis {
68913a9a14fSkettenis 	struct dwhdmi_softc *sc = bridge->driver_private;
69013a9a14fSkettenis 
69113a9a14fSkettenis 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
69213a9a14fSkettenis 		return MODE_BAD;
69313a9a14fSkettenis 
69413a9a14fSkettenis 	if (sc->sc_mode_valid)
69513a9a14fSkettenis 		return sc->sc_mode_valid(sc, mode);
69613a9a14fSkettenis 
69713a9a14fSkettenis 	return MODE_OK;
6986a2cdf39Skettenis }
6996a2cdf39Skettenis 
7006a2cdf39Skettenis const struct drm_bridge_funcs dwhdmi_bridge_funcs = {
7016a2cdf39Skettenis 	.attach = dwhdmi_bridge_attach,
7026a2cdf39Skettenis 	.enable = dwhdmi_bridge_enable,
7036a2cdf39Skettenis 	.pre_enable = dwhdmi_bridge_pre_enable,
7046a2cdf39Skettenis 	.disable = dwhdmi_bridge_disable,
7056a2cdf39Skettenis 	.post_disable = dwhdmi_bridge_post_disable,
7066a2cdf39Skettenis 	.mode_set = dwhdmi_bridge_mode_set,
70713a9a14fSkettenis 	.mode_valid = dwhdmi_bridge_mode_valid,
7086a2cdf39Skettenis };
7096a2cdf39Skettenis 
7106a2cdf39Skettenis #ifdef notyet
7116a2cdf39Skettenis 
7126a2cdf39Skettenis static int
dwhdmi_dai_set_format(audio_dai_tag_t dai,u_int format)7136a2cdf39Skettenis dwhdmi_dai_set_format(audio_dai_tag_t dai, u_int format)
7146a2cdf39Skettenis {
7156a2cdf39Skettenis 	return 0;
7166a2cdf39Skettenis }
7176a2cdf39Skettenis 
7186a2cdf39Skettenis static int
dwhdmi_dai_add_device(audio_dai_tag_t dai,audio_dai_tag_t aux)7196a2cdf39Skettenis dwhdmi_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux)
7206a2cdf39Skettenis {
7216a2cdf39Skettenis 	/* Not supported */
7226a2cdf39Skettenis 	return 0;
7236a2cdf39Skettenis }
7246a2cdf39Skettenis 
7256a2cdf39Skettenis static void
dwhdmi_audio_swvol_codec(audio_filter_arg_t * arg)7266a2cdf39Skettenis dwhdmi_audio_swvol_codec(audio_filter_arg_t *arg)
7276a2cdf39Skettenis {
7286a2cdf39Skettenis 	struct dwhdmi_softc * const sc = arg->context;
7296a2cdf39Skettenis 	const aint_t *src;
7306a2cdf39Skettenis 	aint_t *dst;
7316a2cdf39Skettenis 	u_int sample_count;
7326a2cdf39Skettenis 	u_int i;
7336a2cdf39Skettenis 
7346a2cdf39Skettenis 	src = arg->src;
7356a2cdf39Skettenis 	dst = arg->dst;
7366a2cdf39Skettenis 	sample_count = arg->count * arg->srcfmt->channels;
7376a2cdf39Skettenis 	for (i = 0; i < sample_count; i++) {
7386a2cdf39Skettenis 		aint2_t v = (aint2_t)(*src++);
7396a2cdf39Skettenis 		v = v * sc->sc_swvol / 255;
7406a2cdf39Skettenis 		*dst++ = (aint_t)v;
7416a2cdf39Skettenis 	}
7426a2cdf39Skettenis }
7436a2cdf39Skettenis 
7446a2cdf39Skettenis static int
dwhdmi_audio_set_format(void * priv,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)7456a2cdf39Skettenis dwhdmi_audio_set_format(void *priv, int setmode,
7466a2cdf39Skettenis     const audio_params_t *play, const audio_params_t *rec,
7476a2cdf39Skettenis     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
7486a2cdf39Skettenis {
7496a2cdf39Skettenis 	struct dwhdmi_softc * const sc = priv;
7506a2cdf39Skettenis 
7516a2cdf39Skettenis 	pfil->codec = dwhdmi_audio_swvol_codec;
7526a2cdf39Skettenis 	pfil->context = sc;
7536a2cdf39Skettenis 
7546a2cdf39Skettenis 	return 0;
7556a2cdf39Skettenis }
7566a2cdf39Skettenis 
7576a2cdf39Skettenis static int
dwhdmi_audio_set_port(void * priv,mixer_ctrl_t * mc)7586a2cdf39Skettenis dwhdmi_audio_set_port(void *priv, mixer_ctrl_t *mc)
7596a2cdf39Skettenis {
7606a2cdf39Skettenis 	struct dwhdmi_softc * const sc = priv;
7616a2cdf39Skettenis 
7626a2cdf39Skettenis 	switch (mc->dev) {
7636a2cdf39Skettenis 	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
7646a2cdf39Skettenis 	case DWHDMI_DAI_INPUT_DAC_VOLUME:
7656a2cdf39Skettenis 		sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
7666a2cdf39Skettenis 		return 0;
7676a2cdf39Skettenis 	default:
7686a2cdf39Skettenis 		return ENXIO;
7696a2cdf39Skettenis 	}
7706a2cdf39Skettenis }
7716a2cdf39Skettenis 
7726a2cdf39Skettenis static int
dwhdmi_audio_get_port(void * priv,mixer_ctrl_t * mc)7736a2cdf39Skettenis dwhdmi_audio_get_port(void *priv, mixer_ctrl_t *mc)
7746a2cdf39Skettenis {
7756a2cdf39Skettenis 	struct dwhdmi_softc * const sc = priv;
7766a2cdf39Skettenis 
7776a2cdf39Skettenis 	switch (mc->dev) {
7786a2cdf39Skettenis 	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
7796a2cdf39Skettenis 	case DWHDMI_DAI_INPUT_DAC_VOLUME:
7806a2cdf39Skettenis 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_swvol;
7816a2cdf39Skettenis 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_swvol;
7826a2cdf39Skettenis 		return 0;
7836a2cdf39Skettenis 	default:
7846a2cdf39Skettenis 		return ENXIO;
7856a2cdf39Skettenis 	}
7866a2cdf39Skettenis }
7876a2cdf39Skettenis 
7886a2cdf39Skettenis static int
dwhdmi_audio_query_devinfo(void * priv,mixer_devinfo_t * di)7896a2cdf39Skettenis dwhdmi_audio_query_devinfo(void *priv, mixer_devinfo_t *di)
7906a2cdf39Skettenis {
7916a2cdf39Skettenis 	switch (di->index) {
7926a2cdf39Skettenis 	case DWHDMI_DAI_OUTPUT_CLASS:
7936a2cdf39Skettenis 		di->mixer_class = di->index;
7946a2cdf39Skettenis 		strcpy(di->label.name, AudioCoutputs);
7956a2cdf39Skettenis 		di->type = AUDIO_MIXER_CLASS;
7966a2cdf39Skettenis 		di->next = di->prev = AUDIO_MIXER_LAST;
7976a2cdf39Skettenis 		return 0;
7986a2cdf39Skettenis 
7996a2cdf39Skettenis 	case DWHDMI_DAI_INPUT_CLASS:
8006a2cdf39Skettenis 		di->mixer_class = di->index;
8016a2cdf39Skettenis 		strcpy(di->label.name, AudioCinputs);
8026a2cdf39Skettenis 		di->type = AUDIO_MIXER_CLASS;
8036a2cdf39Skettenis 		di->next = di->prev = AUDIO_MIXER_LAST;
8046a2cdf39Skettenis 		return 0;
8056a2cdf39Skettenis 
8066a2cdf39Skettenis 	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
8076a2cdf39Skettenis 		di->mixer_class = DWHDMI_DAI_OUTPUT_CLASS;
8086a2cdf39Skettenis 		strcpy(di->label.name, AudioNmaster);
8096a2cdf39Skettenis 		di->un.v.delta = 1;
8106a2cdf39Skettenis 		di->un.v.num_channels = 2;
8116a2cdf39Skettenis 		strcpy(di->un.v.units.name, AudioNvolume);
8126a2cdf39Skettenis 		di->type = AUDIO_MIXER_VALUE;
8136a2cdf39Skettenis 		di->next = di->prev = AUDIO_MIXER_LAST;
8146a2cdf39Skettenis 		return 0;
8156a2cdf39Skettenis 
8166a2cdf39Skettenis 	case DWHDMI_DAI_INPUT_DAC_VOLUME:
8176a2cdf39Skettenis 		di->mixer_class = DWHDMI_DAI_INPUT_CLASS;
8186a2cdf39Skettenis 		strcpy(di->label.name, AudioNdac);
8196a2cdf39Skettenis 		di->un.v.delta = 1;
8206a2cdf39Skettenis 		di->un.v.num_channels = 2;
8216a2cdf39Skettenis 		strcpy(di->un.v.units.name, AudioNvolume);
8226a2cdf39Skettenis 		di->type = AUDIO_MIXER_VALUE;
8236a2cdf39Skettenis 		di->next = di->prev = AUDIO_MIXER_LAST;
8246a2cdf39Skettenis 		return 0;
8256a2cdf39Skettenis 
8266a2cdf39Skettenis 	default:
8276a2cdf39Skettenis 		return ENXIO;
8286a2cdf39Skettenis 	}
8296a2cdf39Skettenis }
8306a2cdf39Skettenis 
8316a2cdf39Skettenis static const struct audio_hw_if dwhdmi_dai_hw_if = {
8326a2cdf39Skettenis 	.set_format = dwhdmi_audio_set_format,
8336a2cdf39Skettenis 	.set_port = dwhdmi_audio_set_port,
8346a2cdf39Skettenis 	.get_port = dwhdmi_audio_get_port,
8356a2cdf39Skettenis 	.query_devinfo = dwhdmi_audio_query_devinfo,
8366a2cdf39Skettenis };
8376a2cdf39Skettenis 
8386a2cdf39Skettenis #endif
8396a2cdf39Skettenis 
8406a2cdf39Skettenis int
dwhdmi_attach(struct dwhdmi_softc * sc)8416a2cdf39Skettenis dwhdmi_attach(struct dwhdmi_softc *sc)
8426a2cdf39Skettenis {
8436a2cdf39Skettenis 	uint8_t val;
8446a2cdf39Skettenis 
8456a2cdf39Skettenis 	if (sc->sc_reg_width != 1 && sc->sc_reg_width != 4) {
8466a2cdf39Skettenis 		printf("%s: unsupported register width %d\n",
8476a2cdf39Skettenis 		    sc->sc_dev.dv_xname, sc->sc_reg_width);
8486a2cdf39Skettenis 		return EINVAL;
8496a2cdf39Skettenis 	}
8506a2cdf39Skettenis 
8516a2cdf39Skettenis 	sc->sc_version = dwhdmi_read(sc, HDMI_DESIGN_ID);
8526a2cdf39Skettenis 	sc->sc_version <<= 8;
8536a2cdf39Skettenis 	sc->sc_version |= dwhdmi_read(sc, HDMI_REVISION_ID);
8546a2cdf39Skettenis 
8556a2cdf39Skettenis 	sc->sc_phytype = dwhdmi_read(sc, HDMI_CONFIG2_ID);
8566a2cdf39Skettenis 
8576a2cdf39Skettenis 	printf("%s: version %x.%03x, phytype 0x%02x\n", sc->sc_dev.dv_xname,
8586a2cdf39Skettenis 	    sc->sc_version >> 12, sc->sc_version & 0xfff,
8596a2cdf39Skettenis 	    sc->sc_phytype);
8606a2cdf39Skettenis 
8616a2cdf39Skettenis #ifdef notyet
8626a2cdf39Skettenis 	sc->sc_swvol = 255;
8636a2cdf39Skettenis #endif
8646a2cdf39Skettenis 
8656a2cdf39Skettenis 	/*
8666a2cdf39Skettenis 	 * If a DDC i2c bus tag is provided by the caller, use it. Otherwise,
8676a2cdf39Skettenis 	 * use the I2C master built-in to DWC HDMI.
8686a2cdf39Skettenis 	 */
8696a2cdf39Skettenis 	if (sc->sc_ic == NULL) {
8706a2cdf39Skettenis 		struct i2c_controller *ic = &sc->sc_ic_builtin;
8716a2cdf39Skettenis 
8726a2cdf39Skettenis 		memset(ic, 0, sizeof(*ic));
8736a2cdf39Skettenis 		ic->ic_cookie = sc;
8746a2cdf39Skettenis 		ic->ic_exec = dwhdmi_ddc_exec;
8756a2cdf39Skettenis 		sc->sc_ic = ic;
8766a2cdf39Skettenis 	}
8776a2cdf39Skettenis 
8786a2cdf39Skettenis 	/*
8796a2cdf39Skettenis 	 * Enable HPD on internal PHY
8806a2cdf39Skettenis 	 */
8816a2cdf39Skettenis 	if ((sc->sc_flags & DWHDMI_USE_INTERNAL_PHY) != 0) {
8826a2cdf39Skettenis 		val = dwhdmi_read(sc, HDMI_PHY_CONF0);
8836a2cdf39Skettenis 		val |= HDMI_PHY_CONF0_ENHPDRXSENSE;
8846a2cdf39Skettenis 		dwhdmi_write(sc, HDMI_PHY_CONF0, val);
8856a2cdf39Skettenis 	}
8866a2cdf39Skettenis 
8876a2cdf39Skettenis #ifdef notyet
8886a2cdf39Skettenis 	/*
8896a2cdf39Skettenis 	 * Initialize audio DAI
8906a2cdf39Skettenis 	 */
8916a2cdf39Skettenis 	sc->sc_dai.dai_set_format = dwhdmi_dai_set_format;
8926a2cdf39Skettenis 	sc->sc_dai.dai_add_device = dwhdmi_dai_add_device;
8936a2cdf39Skettenis 	sc->sc_dai.dai_hw_if = &dwhdmi_dai_hw_if;
8946a2cdf39Skettenis 	sc->sc_dai.dai_dev = sc->sc_dev;
8956a2cdf39Skettenis 	sc->sc_dai.dai_priv = sc;
8966a2cdf39Skettenis #endif
8976a2cdf39Skettenis 
8986a2cdf39Skettenis 	return 0;
8996a2cdf39Skettenis }
9006a2cdf39Skettenis 
9016a2cdf39Skettenis int
dwhdmi_bind(struct dwhdmi_softc * sc,struct drm_encoder * encoder)9026a2cdf39Skettenis dwhdmi_bind(struct dwhdmi_softc *sc, struct drm_encoder *encoder)
9036a2cdf39Skettenis {
9046a2cdf39Skettenis 	int error;
9056a2cdf39Skettenis 
9066a2cdf39Skettenis 	sc->sc_bridge.driver_private = sc;
9076a2cdf39Skettenis 	sc->sc_bridge.funcs = &dwhdmi_bridge_funcs;
9086a2cdf39Skettenis 	sc->sc_bridge.encoder = encoder;
9096a2cdf39Skettenis 
910c349dbc7Sjsg 	error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL, 0);
9116a2cdf39Skettenis 	if (error != 0)
9126a2cdf39Skettenis 		return EIO;
9136a2cdf39Skettenis 
9146a2cdf39Skettenis 	return 0;
9156a2cdf39Skettenis }
916