xref: /openbsd-src/sys/dev/ic/dwhdmiphy.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /* $OpenBSD: dwhdmiphy.c,v 1.1 2020/03/02 10:37:23 kettenis Exp $ */
2 /* $NetBSD: dw_hdmi_phy.c,v 1.2 2019/11/10 10:36:01 jmcneill Exp $ */
3 
4 /*-
5  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 
32 #include <drm/drmP.h>
33 
34 #include <dev/ic/dwhdmi.h>
35 
36 #define	HDMI_IH_PHY_STAT0                       0x0104
37 #define	  HDMI_IH_PHY_STAT0_HPD (1 << 0)
38 #define	HDMI_IH_I2CMPHY_STAT0                   0x0108
39 #define	  HDMI_IH_I2CMPHY_STAT0_DONE            (1 << 1)
40 #define	  HDMI_IH_I2CMPHY_STAT0_ERROR           (1 << 0)
41 
42 #define	HDMI_PHY_CONF0				0x3000
43 #define	  HDMI_PHY_CONF0_PDZ_MASK			0x80
44 #define	  HDMI_PHY_CONF0_PDZ_OFFSET		7
45 #define	  HDMI_PHY_CONF0_ENTMDS_MASK		0x40
46 #define	  HDMI_PHY_CONF0_ENTMDS_OFFSET		6
47 #define	  HDMI_PHY_CONF0_SVSRET_MASK		0x20
48 #define	  HDMI_PHY_CONF0_SVSRET_OFFSET			5
49 #define	  HDMI_PHY_CONF0_GEN2_PDDQ_MASK		0x10
50 #define	  HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET		4
51 #define	  HDMI_PHY_CONF0_GEN2_TXPWRON_MASK	0x8
52 #define	  HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET	3
53 #define	  HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK	0x4
54 #define	  HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET	2
55 #define	  HDMI_PHY_CONF0_SELDATAENPOL_MASK	0x2
56 #define	  HDMI_PHY_CONF0_SELDATAENPOL_OFFSET	1
57 #define	  HDMI_PHY_CONF0_SELDIPIF_MASK		0x1
58 #define	  HDMI_PHY_CONF0_SELDIPIF_OFFSET		0
59 #define	HDMI_PHY_TST0				0x3001
60 #define	  HDMI_PHY_TST0_TSTCLR_MASK		0x20
61 #define	  HDMI_PHY_TST0_TSTCLR_OFFSET		5
62 #define	  HDMI_PHY_TST0_TSTEN_MASK		0x10
63 #define	  HDMI_PHY_TST0_TSTEN_OFFSET		4
64 #define	  HDMI_PHY_TST0_TSTCLK_MASK		0x1
65 #define	  HDMI_PHY_TST0_TSTCLK_OFFSET		0
66 #define	HDMI_PHY_TST1				0x3002
67 #define	HDMI_PHY_TST2				0x3003
68 #define	HDMI_PHY_STAT0				0x3004
69 #define	  HDMI_PHY_STAT0_RX_SENSE3		0x80
70 #define	  HDMI_PHY_STAT0_RX_SENSE2		0x40
71 #define	  HDMI_PHY_STAT0_RX_SENSE1		0x20
72 #define	  HDMI_PHY_STAT0_RX_SENSE0		0x10
73 #define	  HDMI_PHY_STAT0_RX_SENSE		0xf0
74 #define	  HDMI_PHY_STAT0_HPD			0x02
75 #define	  HDMI_PHY_TX_PHY_LOCK			0x01
76 #define	HDMI_PHY_INT0				0x3005
77 #define	HDMI_PHY_MASK0				0x3006
78 #define	HDMI_PHY_POL0				0x3007
79 #define	  HDMI_PHY_POL0_HPD			0x02
80 
81 /* HDMI Master PHY Registers */
82 #define	HDMI_PHY_I2CM_SLAVE_ADDR		0x3020
83 #define	  HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2	0x69
84 #define	  HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY	0x49
85 #define	HDMI_PHY_I2CM_ADDRESS_ADDR		0x3021
86 #define	HDMI_PHY_I2CM_DATAO_1_ADDR		0x3022
87 #define	HDMI_PHY_I2CM_DATAO_0_ADDR		0x3023
88 #define	HDMI_PHY_I2CM_DATAI_1_ADDR		0x3024
89 #define	HDMI_PHY_I2CM_DATAI_0_ADDR		0x3025
90 #define	HDMI_PHY_I2CM_OPERATION_ADDR		0x3026
91 #define	  HDMI_PHY_I2CM_OPERATION_ADDR_WRITE    0x10
92 #define	  HDMI_PHY_I2CM_OPERATION_ADDR_READ     0x1
93 #define	HDMI_PHY_I2CM_INT_ADDR			0x3027
94 #define	HDMI_PHY_I2CM_CTLINT_ADDR		0x3028
95 #define	HDMI_PHY_I2CM_DIV_ADDR			0x3029
96 #define	HDMI_PHY_I2CM_SOFTRSTZ_ADDR		0x302a
97 #define	HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR	0x302b
98 #define	HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR	0x302c
99 #define	HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR	0x302d
100 #define	HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR	0x302e
101 #define	HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR	0x302f
102 #define	HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR	0x3030
103 #define	HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR	0x3031
104 #define	HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR	0x3032
105 
106 #define	HDMI_MC_FLOWCTRL                        0x4004
107 #define	  HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK                0x1
108 #define	  HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH 0x1
109 #define	  HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS  0x0
110 #define	HDMI_MC_PHYRSTZ                         0x4005
111 #define	  HDMI_MC_PHYRSTZ_ASSERT                        0x0
112 #define	  HDMI_MC_PHYRSTZ_DEASSERT              0x1
113 #define	HDMI_MC_HEACPHY_RST                     0x4007
114 #define	  HDMI_MC_HEACPHY_RST_ASSERT            0x1
115 #define	  HDMI_MC_HEACPHY_RST_DEASSERT          0x0
116 
117 /* HDMI PHY register with access through I2C */
118 #define	HDMI_PHY_I2C_CKCALCTRL	0x5
119 #define	  CKCALCTRL_OVERRIDE	(1 << 15)
120 #define	HDMI_PHY_I2C_CPCE_CTRL	0x6
121 #define	  CPCE_CTRL_45_25		((3 << 7) | (3 << 5))
122 #define	  CPCE_CTRL_92_50		((2 << 7) | (2 << 5))
123 #define	  CPCE_CTRL_185		((1 << 7) | (1 << 5))
124 #define	  CPCE_CTRL_370		((0 << 7) | (0 << 5))
125 #define	HDMI_PHY_I2C_CKSYMTXCTRL	0x9
126 #define	  CKSYMTXCTRL_OVERRIDE	(1 << 15)
127 #define	  CKSYMTXCTRL_TX_SYMON	(1 << 3)
128 #define	  CKSYMTXCTRL_TX_TRAON	(1 << 2)
129 #define	  CKSYMTXCTRL_TX_TRBON	(1 << 1)
130 #define	  CKSYMTXCTRL_TX_CK_SYMON	(1 << 0)
131 #define	HDMI_PHY_I2C_VLEVCTRL		0x0E
132 #define	HDMI_PHY_I2C_CURRCTRL		0x10
133 #define	HDMI_PHY_I2C_PLLPHBYCTRL	0x13
134 #define	  VLEVCTRL_TX_LVL(x)	((x) << 5)
135 #define	  VLEVCTRL_CK_LVL(x)	(x)
136 #define	HDMI_PHY_I2C_GMPCTRL	0x15
137 #define	  GMPCTRL_45_25		0x00
138 #define	  GMPCTRL_92_50		0x05
139 #define	  GMPCTRL_185		0x0a
140 #define	  GMPCTRL_370		0x0f
141 #define	HDMI_PHY_I2C_MSM_CTRL	0x17
142 #define	  MSM_CTRL_FB_CLK		(0x3 << 1)
143 #define	HDMI_PHY_I2C_TXTERM	0x19
144 #define	  TXTERM_133		0x5
145 
146 void
147 dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc *sc, int msec)
148 {
149 	uint8_t val;
150 
151 	val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) &
152 	    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
153 	while (val == 0) {
154 		delay(1000);
155 		msec -= 10;
156 		if (msec <= 0)
157 			return;
158 		val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) &
159 		    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
160 	}
161 }
162 
163 void
164 dwhdmi_phy_i2c_write(struct dwhdmi_softc *sc, unsigned short data,
165     unsigned char addr)
166 {
167 
168 	/* clear DONE and ERROR flags */
169 	dwhdmi_write(sc, HDMI_IH_I2CMPHY_STAT0,
170 	    HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
171 	dwhdmi_write(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
172 	dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff));
173 	dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff));
174 	dwhdmi_write(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
175 	dwhdmi_phy_wait_i2c_done(sc, 1000);
176 }
177 
178 void
179 dwhdmi_phy_enable_power(struct dwhdmi_softc *sc, uint8_t enable)
180 {
181 	uint8_t reg;
182 
183 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
184 	reg &= ~HDMI_PHY_CONF0_PDZ_MASK;
185 	reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET);
186 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
187 }
188 
189 void
190 dwhdmi_phy_enable_tmds(struct dwhdmi_softc *sc, uint8_t enable)
191 {
192 	uint8_t reg;
193 
194 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
195 	reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK;
196 	reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
197 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
198 }
199 
200 void
201 dwhdmi_phy_gen2_pddq(struct dwhdmi_softc *sc, uint8_t enable)
202 {
203 	uint8_t reg;
204 
205 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
206 	reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
207 	reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
208 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
209 }
210 
211 void
212 dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc *sc, uint8_t enable)
213 {
214 	uint8_t reg;
215 
216 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
217 	reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
218 	reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
219 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
220 }
221 
222 void
223 dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc *sc, uint8_t enable)
224 {
225 	uint8_t reg;
226 
227 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
228 	reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK;
229 	reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
230 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
231 }
232 
233 void
234 dwhdmi_phy_sel_interface_control(struct dwhdmi_softc *sc, uint8_t enable)
235 {
236 	uint8_t reg;
237 
238 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
239 	reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK;
240 	reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
241 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
242 }
243 
244 void
245 dwhdmi_phy_enable_svsret(struct dwhdmi_softc *sc, uint8_t enable)
246 {
247 	uint8_t reg;
248 
249 	reg = dwhdmi_read(sc, HDMI_PHY_CONF0);
250 	reg &= ~HDMI_PHY_CONF0_SVSRET_MASK;
251 	reg |= (enable << HDMI_PHY_CONF0_SVSRET_OFFSET);
252 	dwhdmi_write(sc, HDMI_PHY_CONF0, reg);
253 }
254 
255 void
256 dwhdmi_phy_test_clear(struct dwhdmi_softc *sc, unsigned char bit)
257 {
258 	uint8_t val;
259 
260 	val = dwhdmi_read(sc, HDMI_PHY_TST0);
261 	val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
262 	val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
263 		HDMI_PHY_TST0_TSTCLR_MASK;
264 	dwhdmi_write(sc, HDMI_PHY_TST0, val);
265 }
266 
267 int
268 dwhdmi_phy_configure(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
269 {
270 	const struct dwhdmi_mpll_config *mpll_conf;
271 	const struct dwhdmi_phy_config *phy_conf;
272 	uint8_t val;
273 	uint8_t msec;
274 
275 	dwhdmi_write(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS);
276 
277 	/* gen2 tx power off */
278 	dwhdmi_phy_gen2_txpwron(sc, 0);
279 
280 	/* gen2 pddq */
281 	dwhdmi_phy_gen2_pddq(sc, 1);
282 
283 	/* PHY reset */
284 	dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
285 	dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT);
286 
287 	dwhdmi_write(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT);
288 
289 	dwhdmi_phy_test_clear(sc, 1);
290 	dwhdmi_write(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
291 	dwhdmi_phy_test_clear(sc, 0);
292 
293 	/*
294 	 * Following initialization are for 8bit per color case
295 	 */
296 
297 	/*
298 	 * PLL/MPLL config
299 	 */
300 	for (mpll_conf = &sc->sc_mpll_config[0]; mpll_conf->pixel_clock != 0; mpll_conf++)
301 		if (mode->clock <= mpll_conf->pixel_clock)
302 			break;
303 
304 	dwhdmi_phy_i2c_write(sc, mpll_conf->cpce, HDMI_PHY_I2C_CPCE_CTRL);
305 	dwhdmi_phy_i2c_write(sc, mpll_conf->gmp, HDMI_PHY_I2C_GMPCTRL);
306 	dwhdmi_phy_i2c_write(sc, mpll_conf->curr, HDMI_PHY_I2C_CURRCTRL);
307 
308 	for (phy_conf = &sc->sc_phy_config[0]; phy_conf->pixel_clock != 0; phy_conf++)
309 		if (mode->clock <= phy_conf->pixel_clock)
310 			break;
311 
312 	dwhdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL);
313 	dwhdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL);
314 
315 	dwhdmi_phy_i2c_write(sc, phy_conf->term, HDMI_PHY_I2C_TXTERM);
316 	dwhdmi_phy_i2c_write(sc, phy_conf->sym, HDMI_PHY_I2C_CKSYMTXCTRL);
317 	dwhdmi_phy_i2c_write(sc, phy_conf->vlev, HDMI_PHY_I2C_VLEVCTRL);
318 
319 	/* REMOVE CLK TERM */
320 	dwhdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL);
321 
322 	dwhdmi_phy_enable_power(sc, 1);
323 
324 	/* toggle TMDS enable */
325 	dwhdmi_phy_enable_tmds(sc, 0);
326 	dwhdmi_phy_enable_tmds(sc, 1);
327 
328 	/* gen2 tx power on */
329 	dwhdmi_phy_gen2_txpwron(sc, 1);
330 	dwhdmi_phy_gen2_pddq(sc, 0);
331 
332 	switch (sc->sc_phytype) {
333 	case 0xb2:	/* MHL PHY HEAC */
334 	case 0xc2:	/* MHL PHY */
335 	case 0xf3:	/* HDMI 2.0 TX PHY */
336 		dwhdmi_phy_enable_svsret(sc, 1);
337 		break;
338 	}
339 
340 	/*Wait for PHY PLL lock */
341 	msec = 4;
342 	val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
343 	while (val == 0) {
344 		delay(1000);
345 		if (msec-- == 0) {
346 			printf("%s: PHY PLL not locked\n",
347 			    sc->sc_dev.dv_xname);
348 			return (-1);
349 		}
350 		val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
351 	}
352 
353 	return (0);
354 }
355 
356 void
357 dwhdmi_phy_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode)
358 {
359 	int i;
360 
361 	/* HDMI Phy spec says to do the phy initialization sequence twice */
362 	for (i = 0 ; i < 2 ; i++) {
363 		dwhdmi_phy_sel_data_en_pol(sc, 1);
364 		dwhdmi_phy_sel_interface_control(sc, 0);
365 		dwhdmi_phy_enable_tmds(sc, 0);
366 		dwhdmi_phy_enable_power(sc, 0);
367 
368 		/* Enable CSC */
369 		dwhdmi_phy_configure(sc, mode);
370 	}
371 }
372 
373 enum drm_connector_status
374 dwhdmi_phy_detect(struct dwhdmi_softc *sc, int force)
375 {
376 	uint8_t val;
377 
378 	val = dwhdmi_read(sc, HDMI_PHY_STAT0);
379 
380 	return ((val & HDMI_PHY_STAT0_HPD) != 0) ?
381 	    connector_status_connected :
382 	    connector_status_disconnected;
383 }
384 
385 void
386 dwhdmi_phy_enable(struct dwhdmi_softc *sc)
387 {
388 }
389 
390 void
391 dwhdmi_phy_disable(struct dwhdmi_softc *sc)
392 {
393 }
394 
395 void
396 dwhdmi_phy_mode_set(struct dwhdmi_softc *sc,
397     struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
398 {
399 	dwhdmi_phy_init(sc, adjusted_mode);
400 }
401