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
144 void
dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc * sc,int msec)145 dwhdmi_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
161 void
dwhdmi_phy_i2c_write(struct dwhdmi_softc * sc,unsigned short data,unsigned char addr)162 dwhdmi_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
176 void
dwhdmi_phy_enable_power(struct dwhdmi_softc * sc,uint8_t enable)177 dwhdmi_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
187 void
dwhdmi_phy_enable_tmds(struct dwhdmi_softc * sc,uint8_t enable)188 dwhdmi_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
198 void
dwhdmi_phy_gen2_pddq(struct dwhdmi_softc * sc,uint8_t enable)199 dwhdmi_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
209 void
dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc * sc,uint8_t enable)210 dwhdmi_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
220 void
dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc * sc,uint8_t enable)221 dwhdmi_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
231 void
dwhdmi_phy_sel_interface_control(struct dwhdmi_softc * sc,uint8_t enable)232 dwhdmi_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
242 void
dwhdmi_phy_enable_svsret(struct dwhdmi_softc * sc,uint8_t enable)243 dwhdmi_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
253 void
dwhdmi_phy_test_clear(struct dwhdmi_softc * sc,unsigned char bit)254 dwhdmi_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
265 int
dwhdmi_phy_configure(struct dwhdmi_softc * sc,const struct drm_display_mode * mode)266 dwhdmi_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
354 void
dwhdmi_phy_init(struct dwhdmi_softc * sc,const struct drm_display_mode * mode)355 dwhdmi_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
371 enum drm_connector_status
dwhdmi_phy_detect(struct dwhdmi_softc * sc,int force)372 dwhdmi_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
383 void
dwhdmi_phy_enable(struct dwhdmi_softc * sc)384 dwhdmi_phy_enable(struct dwhdmi_softc *sc)
385 {
386 }
387
388 void
dwhdmi_phy_disable(struct dwhdmi_softc * sc)389 dwhdmi_phy_disable(struct dwhdmi_softc *sc)
390 {
391 }
392
393 void
dwhdmi_phy_mode_set(struct dwhdmi_softc * sc,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)394 dwhdmi_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