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