sunxi_hdmiphy.c revision 1.2 1 /* $NetBSD: sunxi_hdmiphy.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca>
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30
31 __KERNEL_RCSID(0, "$NetBSD: sunxi_hdmiphy.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $");
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/device.h>
36 #include <sys/intr.h>
37 #include <sys/systm.h>
38
39 #include <dev/fdt/fdtvar.h>
40
41 #include <arm/sunxi/sunxi_hdmiphy.h>
42
43 #define DBG_CTRL 0x000
44 #define DBG_CTRL_POL __BITS(15,8)
45 #define DBG_CTRL_POL_NVSYNC 1
46 #define DBG_CTRL_POL_NHSYNC 2
47
48 #define READ_EN 0x010
49 #define READ_EN_MAGIC 0x54524545 /* "TREE" */
50
51 #define UNSCRAMBLE 0x014
52 #define UNSCRAMBLE_MAGIC 0x42494E47 /* "BING" */
53
54 #define ANA_CFG1 0x020
55 #define ANA_CFG1_ENRCAL __BIT(19)
56 #define ANA_CFG1_ENCALOG __BIT(18)
57 #define ANA_CFG1_TMDSCLK_EN __BIT(16)
58 #define ANA_CFG1_TXEN __BITS(15,12)
59 #define ANA_CFG1_BIASEN __BITS(11,8)
60 #define ANA_CFG1_ENP2S __BITS(7,4)
61 #define ANA_CFG1_CKEN __BIT(3)
62 #define ANA_CFG1_LDOEN __BIT(2)
63 #define ANA_CFG1_ENVBS __BIT(1)
64 #define ANA_CFG1_ENBI __BIT(0)
65
66 #define ANA_CFG2 0x024
67 #define ANA_CFG2_REG_RESDI __BITS(5,0)
68
69 #define ANA_CFG3 0x028
70 #define ANA_CFG3_REG_SDAEN __BIT(2)
71 #define ANA_CFG3_REG_SCLEN __BIT(0)
72
73 #define PLL_CFG1 0x02c
74 #define PLL_CFG1_REG_OD1 __BIT(31)
75 #define PLL_CFG1_REG_OD0 __BIT(30)
76 #define PLL_CFG1_CKIN_SEL __BIT(26)
77 #define PLL_CFG1_PLLEN __BIT(25)
78 #define PLL_CFG1_B_IN __BITS(5,0)
79
80 #define PLL_CFG2 0x030
81 #define PLL_CFG2_PREDIV __BITS(3,0)
82
83 #define PLL_CFG3 0x034
84
85 #define ANA_STS 0x038
86 #define ANA_STS_HPDO __BIT(19)
87 #define ANA_STS_B_OUT __BITS(16,11)
88 #define ANA_STS_RCALEND2D __BIT(7)
89 #define ANA_STS_RESDO2D __BITS(5,0)
90
91 #define CEC 0x03c
92 #define CEC_CONTROL_SEL __BIT(7)
93 #define CEC_INPUT_DATA __BIT(1)
94 #define CEC_OUTPUT_DATA __BIT(0)
95
96 #define CONTROLLER_VER 0xff8
97
98 #define PHY_VER 0xffc
99
100 struct sunxi_hdmiphy_softc;
101
102 static int sunxi_hdmiphy_match(device_t, cfdata_t, void *);
103 static void sunxi_hdmiphy_attach(device_t, device_t, void *);
104
105 static void sun8i_h3_hdmiphy_init(struct sunxi_hdmiphy_softc *);
106 static int sun8i_h3_hdmiphy_config(struct sunxi_hdmiphy_softc *, u_int);
107
108 struct sunxi_hdmiphy_data {
109 void (*init)(struct sunxi_hdmiphy_softc *);
110 int (*config)(struct sunxi_hdmiphy_softc *, u_int);
111 };
112
113 static const struct sunxi_hdmiphy_data sun8i_h3_hdmiphy_data = {
114 .init = sun8i_h3_hdmiphy_init,
115 .config = sun8i_h3_hdmiphy_config,
116 };
117
118 static const struct of_compat_data compat_data[] = {
119 { "allwinner,sun8i-h3-hdmi-phy", (uintptr_t)&sun8i_h3_hdmiphy_data },
120 { "allwinner,sun50i-a64-hdmi-phy", (uintptr_t)&sun8i_h3_hdmiphy_data },
121 { NULL }
122 };
123
124 struct sunxi_hdmiphy_softc {
125 device_t sc_dev;
126 bus_space_tag_t sc_bst;
127 bus_space_handle_t sc_bsh;
128
129 const struct sunxi_hdmiphy_data *sc_data;
130
131 struct clk *sc_clk_pll0;
132
133 u_int sc_rcalib;
134 };
135
136 #define PHY_READ(sc, reg) \
137 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
138 #define PHY_WRITE(sc, reg, val) \
139 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
140 #define PHY_SET_CLEAR(sc, reg, set, clr) \
141 do { \
142 uint32_t _tval = PHY_READ((sc), (reg)); \
143 _tval &= ~(clr); \
144 _tval |= (set); \
145 PHY_WRITE((sc), (reg), _tval); \
146 } while (0)
147 #define PHY_SET(sc, reg, set) \
148 PHY_SET_CLEAR(sc, reg, set, 0)
149 #define PHY_CLEAR(sc, reg, clr) \
150 PHY_SET_CLEAR(sc, reg, 0, clr)
151
152 CFATTACH_DECL_NEW(sunxi_hdmiphy, sizeof(struct sunxi_hdmiphy_softc),
153 sunxi_hdmiphy_match, sunxi_hdmiphy_attach, NULL, NULL);
154
155 static void *
156 sunxi_hdmiphy_acquire(device_t dev, const void *data, size_t len)
157 {
158 struct sunxi_hdmiphy_softc * const sc = device_private(dev);
159
160 if (len != 0)
161 return NULL;
162
163 return sc;
164 }
165
166 static void
167 sunxi_hdmiphy_release(device_t dev, void *priv)
168 {
169 }
170
171 static int
172 sunxi_hdmiphy_enable(device_t dev, void *priv, bool enable)
173 {
174 struct sunxi_hdmiphy_softc * const sc = priv;
175
176 if (enable) {
177 sc->sc_data->init(sc);
178 } else {
179 sc->sc_data->config(sc, 0);
180 }
181
182 return 0;
183 }
184
185 static const struct fdtbus_phy_controller_func sunxi_hdmiphy_funcs = {
186 .acquire = sunxi_hdmiphy_acquire,
187 .release = sunxi_hdmiphy_release,
188 .enable = sunxi_hdmiphy_enable,
189 };
190
191 #ifdef SUNXI_HDMIPHY_DEBUG
192 static void
193 sunxi_hdmiphy_dump(struct sunxi_hdmiphy_softc *sc)
194 {
195 device_printf(sc->sc_dev, "ANA_CFG1: %#x\tANA_CFG2: %#x\tANA_CFG3: %#x\n",
196 PHY_READ(sc, ANA_CFG1), PHY_READ(sc, ANA_CFG2), PHY_READ(sc, ANA_CFG3));
197 device_printf(sc->sc_dev, "PLL_CFG1: %#x\tPLL_CFG2: %#x\tPLL_CFG3: %#x\n",
198 PHY_READ(sc, PLL_CFG1), PHY_READ(sc, PLL_CFG2), PHY_READ(sc, PLL_CFG3));
199 device_printf(sc->sc_dev, "DBG_CTRL: %#x\tANA_STS: %#x\n",
200 PHY_READ(sc, DBG_CTRL), PHY_READ(sc, ANA_STS));
201 }
202 #endif
203
204 static void
205 sun8i_h3_hdmiphy_init(struct sunxi_hdmiphy_softc *sc)
206 {
207 uint32_t val;
208 int retry;
209
210 PHY_WRITE(sc, ANA_CFG1, 0);
211
212 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENBI);
213 delay(5);
214
215 /* Enable TMDS clock */
216 PHY_SET(sc, ANA_CFG1, ANA_CFG1_TMDSCLK_EN);
217
218 /* Enable common voltage reference bias module */
219 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENVBS);
220 delay(20);
221
222 /* Enable internal LDO */
223 PHY_SET(sc, ANA_CFG1, ANA_CFG1_LDOEN);
224 delay(5);
225
226 /* Enable common clock module */
227 PHY_SET(sc, ANA_CFG1, ANA_CFG1_CKEN);
228 delay(100);
229
230 /* Enable resistance calibration analog and digital modules */
231 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENRCAL);
232 delay(200);
233 PHY_SET(sc, ANA_CFG1, ANA_CFG1_ENCALOG);
234
235 /* P2S module enable for TMDS data lane */
236 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0x7, ANA_CFG1_ENP2S), ANA_CFG1_ENP2S);
237
238 /* Wait for resistance calibration to finish */
239 for (retry = 2000; retry > 0; retry--) {
240 if ((PHY_READ(sc, ANA_STS) & ANA_STS_RCALEND2D) != 0)
241 break;
242 delay(1);
243 }
244 if (retry == 0)
245 aprint_error_dev(sc->sc_dev, "HDMI PHY resistance calibration timed out\n");
246
247 /* Enable current and voltage module */
248 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0xf, ANA_CFG1_BIASEN), ANA_CFG1_BIASEN);
249
250 /* P2S module enable for TMDS clock lane */
251 PHY_SET_CLEAR(sc, ANA_CFG1, __SHIFTIN(0xf, ANA_CFG1_ENP2S), ANA_CFG1_ENP2S);
252
253 /* Enable DDC */
254 PHY_SET(sc, ANA_CFG3, ANA_CFG3_REG_SDAEN | ANA_CFG3_REG_SCLEN);
255
256 /* Set parent clock to videopll0 */
257 PHY_CLEAR(sc, PLL_CFG1, PLL_CFG1_CKIN_SEL);
258
259 /* Clear software control of CEC pins */
260 PHY_CLEAR(sc, CEC, CEC_CONTROL_SEL);
261
262 /* Read calibration value for source termination resistors */
263 val = PHY_READ(sc, ANA_STS);
264 sc->sc_rcalib = __SHIFTOUT(val, ANA_STS_RESDO2D);
265 }
266
267 /*
268 * The following table is based on data from the "HDMI TX PHY S40 Specification".
269 */
270 static const struct sun8i_h3_hdmiphy_init {
271 /* PLL Recommended Configuration */
272 uint32_t pll_cfg1;
273 uint32_t pll_cfg2;
274 uint32_t pll_cfg3;
275 /* TMDS Characteristics Recommended Configuration */
276 uint32_t ana_cfg1;
277 uint32_t ana_cfg2;
278 uint32_t ana_cfg3;
279 bool ana_cfg2_rcal_200;
280 u_int b_offset;
281 } sun8i_h3_hdmiphy_inittab[] = {
282 /* 27 MHz */
283 [0] = {
284 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x8008430a, .pll_cfg3 = 0x1,
285 .ana_cfg1 = 0x11ffff7f, .ana_cfg2 = 0x80623000, .ana_cfg3 = 0x0f80c285,
286 .ana_cfg2_rcal_200 = true,
287 },
288 /* 74.25 MHz */
289 [1] = {
290 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x80084343, .pll_cfg3 = 0x1,
291 .ana_cfg1 = 0x11ffff7f, .ana_cfg2 = 0x80623000, .ana_cfg3 = 0x0f814385,
292 .ana_cfg2_rcal_200 = true,
293 },
294 /* 148.5 MHz */
295 [2] = {
296 .pll_cfg1 = 0x3ddc5040, .pll_cfg2 = 0x80084381, .pll_cfg3 = 0x1,
297 .ana_cfg1 = 0x01ffff7f, .ana_cfg2 = 0x8063a800, .ana_cfg3 = 0x0f81c485,
298 },
299 /* 297 MHz */
300 [3] = {
301 .pll_cfg1 = 0x35dc5fc0, .pll_cfg2 = 0x800863c0, .pll_cfg3 = 0x1,
302 .ana_cfg1 = 0x01ffff7f, .ana_cfg2 = 0x8063b000, .ana_cfg3 = 0x0f8246b5,
303 .b_offset = 2,
304 },
305 };
306
307 static int
308 sun8i_h3_hdmiphy_config(struct sunxi_hdmiphy_softc *sc, u_int rate)
309 {
310 const struct sun8i_h3_hdmiphy_init *inittab;
311 u_int init_index, b_out, prediv;
312 uint32_t val, rcalib;
313
314 if (rate == 0) {
315 /* Disable the PHY */
316 PHY_WRITE(sc, ANA_CFG1, ANA_CFG1_LDOEN | ANA_CFG1_ENVBS | ANA_CFG1_ENBI);
317 PHY_WRITE(sc, PLL_CFG1, 0);
318 return 0;
319 }
320
321 init_index = 0;
322 if (rate > 27000000)
323 init_index++;
324 if (rate > 74250000)
325 init_index++;
326 if (rate > 148500000)
327 init_index++;
328 inittab = &sun8i_h3_hdmiphy_inittab[init_index];
329
330 val = PHY_READ(sc, PLL_CFG2);
331 prediv = val & PLL_CFG2_PREDIV;
332
333 /* Config PLL */
334 PHY_WRITE(sc, PLL_CFG1, inittab->pll_cfg1 & ~PLL_CFG1_CKIN_SEL);
335 PHY_WRITE(sc, PLL_CFG2, (inittab->pll_cfg2 & ~PLL_CFG2_PREDIV) | prediv);
336 delay(15000);
337 PHY_WRITE(sc, PLL_CFG3, inittab->pll_cfg3);
338
339 /* Enable PLL */
340 PHY_SET(sc, PLL_CFG1, PLL_CFG1_PLLEN);
341 delay(100000);
342
343 /* Config PLL */
344 val = PHY_READ(sc, ANA_STS);
345 b_out = __SHIFTOUT(val, ANA_STS_B_OUT);
346 b_out = MIN(b_out + inittab->b_offset, __SHIFTOUT_MASK(ANA_STS_B_OUT));
347
348 PHY_SET(sc, PLL_CFG1, PLL_CFG1_REG_OD1 | PLL_CFG1_REG_OD0);
349 PHY_SET(sc, PLL_CFG1, __SHIFTIN(b_out, PLL_CFG1_B_IN));
350 delay(100000);
351
352 /* Config TMDS characteristics */
353 if (inittab->ana_cfg2_rcal_200)
354 rcalib = sc->sc_rcalib >> 2;
355 else
356 rcalib = 0;
357 PHY_WRITE(sc, ANA_CFG1, inittab->ana_cfg1);
358 PHY_WRITE(sc, ANA_CFG2, inittab->ana_cfg2 | rcalib);
359 PHY_WRITE(sc, ANA_CFG3, inittab->ana_cfg3);
360
361 #ifdef SUNXI_HDMIPHY_DEBUG
362 sunxi_hdmiphy_dump(sc);
363 #endif
364
365 return 0;
366 }
367
368 static int
369 sunxi_hdmiphy_set_rate(struct sunxi_hdmiphy_softc *sc, u_int new_rate)
370 {
371 u_int prediv, best_prediv, best_rate;
372
373 if (sc->sc_clk_pll0 == NULL)
374 return 0;
375
376 const u_int parent_rate = clk_get_rate(sc->sc_clk_pll0);
377
378 best_rate = 0;
379
380 for (prediv = 0; prediv <= __SHIFTOUT_MASK(PLL_CFG2_PREDIV); prediv++) {
381 const u_int tmp_rate = parent_rate / (prediv + 1);
382 const int diff = new_rate - tmp_rate;
383 if (diff >= 0 && tmp_rate > best_rate) {
384 best_rate = tmp_rate;
385 best_prediv = prediv;
386 }
387 }
388
389 if (best_rate == 0)
390 return ERANGE;
391
392 PHY_SET_CLEAR(sc, PLL_CFG2, __SHIFTIN(best_prediv, PLL_CFG2_PREDIV), PLL_CFG2_PREDIV);
393
394 return 0;
395 }
396
397 static int
398 sunxi_hdmiphy_match(device_t parent, cfdata_t cf, void *aux)
399 {
400 struct fdt_attach_args * const faa = aux;
401
402 return of_match_compat_data(faa->faa_phandle, compat_data);
403 }
404
405 static void
406 sunxi_hdmiphy_attach(device_t parent, device_t self, void *aux)
407 {
408 struct sunxi_hdmiphy_softc * const sc = device_private(self);
409 struct fdt_attach_args * const faa = aux;
410 const int phandle = faa->faa_phandle;
411 struct clk *clk_bus, *clk_mod, *clk_pll0;
412 struct fdtbus_reset *rst;
413 bus_addr_t addr;
414 bus_size_t size;
415
416 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
417 aprint_error(": couldn't get registers\n");
418 return;
419 }
420
421 rst = fdtbus_reset_get(phandle, "phy");
422 if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
423 aprint_error(": couldn't de-assert reset\n");
424 return;
425 }
426
427 clk_bus = fdtbus_clock_get(phandle, "bus");
428 if (clk_bus == NULL || clk_enable(clk_bus) != 0) {
429 aprint_error(": couldn't enable bus clock\n");
430 return;
431 }
432
433 clk_mod = fdtbus_clock_get(phandle, "mod");
434 if (clk_mod == NULL || clk_enable(clk_mod) != 0) {
435 aprint_error(": couldn't enable mod clock\n");
436 return;
437 }
438
439 clk_pll0 = fdtbus_clock_get(phandle, "pll-0");
440 if (clk_pll0 == NULL || clk_enable(clk_pll0) != 0) {
441 aprint_error(": couldn't enable pll-0 clock\n");
442 return;
443 }
444
445 sc->sc_dev = self;
446 sc->sc_bst = faa->faa_bst;
447 sc->sc_data = (void *)of_search_compatible(phandle, compat_data)->data;
448 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
449 aprint_error(": couldn't map registers\n");
450 return;
451 }
452 sc->sc_clk_pll0 = clk_pll0;
453
454 aprint_naive("\n");
455 aprint_normal(": HDMI PHY\n");
456
457 fdtbus_register_phy_controller(self, phandle, &sunxi_hdmiphy_funcs);
458
459 PHY_WRITE(sc, READ_EN, READ_EN_MAGIC);
460 PHY_WRITE(sc, UNSCRAMBLE, UNSCRAMBLE_MAGIC);
461
462 #ifdef SUNXI_HDMIPHY_DEBUG
463 sunxi_hdmiphy_dump(sc);
464 #endif
465 }
466
467 int
468 sunxi_hdmiphy_config(struct fdtbus_phy *phy, struct drm_display_mode *mode)
469 {
470 device_t dev = fdtbus_phy_device(phy);
471 struct sunxi_hdmiphy_softc * const sc = device_private(dev);
472 u_int pol;
473 int error;
474
475 pol = 0;
476 if ((mode->flags & DRM_MODE_FLAG_NHSYNC) != 0)
477 pol |= __SHIFTIN(DBG_CTRL_POL_NHSYNC, DBG_CTRL_POL);
478 if ((mode->flags & DRM_MODE_FLAG_NVSYNC) != 0)
479 pol |= __SHIFTIN(DBG_CTRL_POL_NVSYNC, DBG_CTRL_POL);
480
481 PHY_SET_CLEAR(sc, DBG_CTRL, pol, DBG_CTRL_POL);
482
483 error = sunxi_hdmiphy_set_rate(sc, mode->crtc_clock * 1000);
484 if (error != 0) {
485 aprint_error_dev(dev, "failed to set HDMI PHY clock: %d\n", error);
486 return error;
487 }
488
489 return sc->sc_data->config(sc, mode->crtc_clock * 1000);
490 }
491
492 bool
493 sunxi_hdmiphy_detect(struct fdtbus_phy *phy, bool force)
494 {
495 device_t dev = fdtbus_phy_device(phy);
496 struct sunxi_hdmiphy_softc * const sc = device_private(dev);
497 uint32_t val;
498
499 val = PHY_READ(sc, ANA_STS);
500
501 return ISSET(val, ANA_STS_HPDO);
502 }
503