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