sunxi_hdmi.c revision 1.7 1 /* $NetBSD: sunxi_hdmi.c,v 1.7 2019/07/19 10:54:26 bouyer Exp $ */
2
3 /*-
4 * Copyright (c) 2014 Jared D. 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * 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 "opt_ddb.h"
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: sunxi_hdmi.c,v 1.7 2019/07/19 10:54:26 bouyer Exp $");
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/device.h>
37 #include <sys/intr.h>
38 #include <sys/kmem.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/proc.h>
42 #include <sys/mutex.h>
43 #include <sys/kthread.h>
44
45 #include <dev/fdt/fdtvar.h>
46 #include <dev/fdt/fdt_port.h>
47
48 #include <dev/i2c/i2cvar.h>
49 #include <dev/i2c/ddcvar.h>
50 #include <dev/i2c/ddcreg.h>
51 #include <dev/videomode/videomode.h>
52 #include <dev/videomode/edidvar.h>
53
54 #include <arm/sunxi/sunxi_hdmireg.h>
55 #include <arm/sunxi/sunxi_display.h>
56
57 enum sunxi_hdmi_type {
58 HDMI_A10 = 1,
59 HDMI_A31,
60 };
61
62 struct sunxi_hdmi_softc {
63 device_t sc_dev;
64 int sc_phandle;
65 enum sunxi_hdmi_type sc_type;
66 bus_space_tag_t sc_bst;
67 bus_space_handle_t sc_bsh;
68 struct clk *sc_clk_ahb;
69 struct clk *sc_clk_mod;
70 struct clk *sc_clk_pll0;
71 struct clk *sc_clk_pll1;
72 void *sc_ih;
73 lwp_t *sc_thread;
74
75 struct i2c_controller sc_ic;
76 kmutex_t sc_ic_lock;
77
78 bool sc_display_connected;
79 char sc_display_vendor[16];
80 char sc_display_product[16];
81
82 u_int sc_display_mode;
83 u_int sc_current_display_mode;
84 #define DISPLAY_MODE_AUTO 0
85 #define DISPLAY_MODE_HDMI 1
86 #define DISPLAY_MODE_DVI 2
87
88 kmutex_t sc_pwr_lock;
89 int sc_pwr_refcount; /* reference who needs HDMI */
90
91 uint32_t sc_ver;
92 unsigned int sc_i2c_blklen;
93
94 struct fdt_device_ports sc_ports;
95 struct fdt_endpoint *sc_in_ep;
96 struct fdt_endpoint *sc_in_rep;
97 struct fdt_endpoint *sc_out_ep;
98 };
99
100 #define HDMI_READ(sc, reg) \
101 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
102 #define HDMI_WRITE(sc, reg, val) \
103 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val));
104
105 #define HDMI_1_3_P(sc) ((sc)->sc_ver == 0x00010003)
106 #define HDMI_1_4_P(sc) ((sc)->sc_ver == 0x00010004)
107
108 static const struct of_compat_data compat_data[] = {
109 {"allwinner,sun4i-a10-hdmi", HDMI_A10},
110 {"allwinner,sun7i-a20-hdmi", HDMI_A10},
111 {NULL}
112 };
113
114 static int sunxi_hdmi_match(device_t, cfdata_t, void *);
115 static void sunxi_hdmi_attach(device_t, device_t, void *);
116 static void sunxi_hdmi_i2c_init(struct sunxi_hdmi_softc *);
117 static int sunxi_hdmi_i2c_acquire_bus(void *, int);
118 static void sunxi_hdmi_i2c_release_bus(void *, int);
119 static int sunxi_hdmi_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
120 size_t, void *, size_t, int);
121 static int sunxi_hdmi_i2c_xfer(void *, i2c_addr_t, uint8_t, uint8_t,
122 size_t, int, int);
123 static int sunxi_hdmi_i2c_reset(struct sunxi_hdmi_softc *, int);
124
125 static int sunxi_hdmi_ep_activate(device_t, struct fdt_endpoint *, bool);
126 static int sunxi_hdmi_ep_enable(device_t, struct fdt_endpoint *, bool);
127 static void sunxi_hdmi_do_enable(struct sunxi_hdmi_softc *);
128 static void sunxi_hdmi_read_edid(struct sunxi_hdmi_softc *);
129 static int sunxi_hdmi_read_edid_block(struct sunxi_hdmi_softc *, uint8_t *,
130 uint8_t);
131 static u_int sunxi_hdmi_get_display_mode(struct sunxi_hdmi_softc *,
132 const struct edid_info *);
133 static void sunxi_hdmi_video_enable(struct sunxi_hdmi_softc *, bool);
134 static void sunxi_hdmi_set_videomode(struct sunxi_hdmi_softc *,
135 const struct videomode *, u_int);
136 static void sunxi_hdmi_set_audiomode(struct sunxi_hdmi_softc *,
137 const struct videomode *, u_int);
138 static void sunxi_hdmi_hpd(struct sunxi_hdmi_softc *);
139 static void sunxi_hdmi_thread(void *);
140 static int sunxi_hdmi_poweron(struct sunxi_hdmi_softc *, bool);
141 #if 0
142 static int sunxi_hdmi_intr(void *);
143 #endif
144
145 #if defined(DDB)
146 void sunxi_hdmi_dump_regs(void);
147 #endif
148
149 CFATTACH_DECL_NEW(sunxi_hdmi, sizeof(struct sunxi_hdmi_softc),
150 sunxi_hdmi_match, sunxi_hdmi_attach, NULL, NULL);
151
152 static int
153 sunxi_hdmi_match(device_t parent, cfdata_t cf, void *aux)
154 {
155 struct fdt_attach_args * const faa = aux;
156
157 return of_match_compat_data(faa->faa_phandle, compat_data);
158 }
159
160 static void
161 sunxi_hdmi_attach(device_t parent, device_t self, void *aux)
162 {
163 struct sunxi_hdmi_softc *sc = device_private(self);
164 struct fdt_attach_args * const faa = aux;
165 const int phandle = faa->faa_phandle;
166 bus_addr_t addr;
167 bus_size_t size;
168 uint32_t ver;
169
170 sc->sc_dev = self;
171 sc->sc_phandle = phandle;
172 sc->sc_bst = faa->faa_bst;
173
174 sc->sc_type = of_search_compatible(faa->faa_phandle, compat_data)->data;
175
176 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
177 aprint_error(": couldn't get registers\n");
178 }
179 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
180 aprint_error(": couldn't map registers\n");
181 return;
182 }
183
184 sc->sc_clk_ahb = fdtbus_clock_get(phandle, "ahb");
185 sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod");
186 sc->sc_clk_pll0 = fdtbus_clock_get(phandle, "pll-0");
187 sc->sc_clk_pll1 = fdtbus_clock_get(phandle, "pll-1");
188
189 if (sc->sc_clk_ahb == NULL || sc->sc_clk_mod == NULL
190 || sc->sc_clk_pll0 == NULL || sc->sc_clk_pll1 == NULL) {
191 aprint_error(": couldn't get clocks\n");
192 aprint_debug_dev(self, "clk ahb %s mod %s pll-0 %s pll-1 %s\n",
193 sc->sc_clk_ahb == NULL ? "missing" : "present",
194 sc->sc_clk_mod == NULL ? "missing" : "present",
195 sc->sc_clk_pll0 == NULL ? "missing" : "present",
196 sc->sc_clk_pll1 == NULL ? "missing" : "present");
197 return;
198 }
199
200 if (clk_enable(sc->sc_clk_ahb) != 0) {
201 aprint_error(": couldn't enable ahb clock\n");
202 return;
203 }
204 ver = HDMI_READ(sc, SUNXI_HDMI_VERSION_ID_REG);
205
206 const int vmaj = __SHIFTOUT(ver, SUNXI_HDMI_VERSION_ID_H);
207 const int vmin = __SHIFTOUT(ver, SUNXI_HDMI_VERSION_ID_L);
208
209 aprint_naive("\n");
210 aprint_normal(": HDMI %d.%d\n", vmaj, vmin);
211
212 sc->sc_ver = ver;
213 sc->sc_i2c_blklen = 16;
214
215 sc->sc_ports.dp_ep_activate = sunxi_hdmi_ep_activate;
216 sc->sc_ports.dp_ep_enable = sunxi_hdmi_ep_enable;
217 fdt_ports_register(&sc->sc_ports, self, phandle, EP_OTHER);
218
219 mutex_init(&sc->sc_pwr_lock, MUTEX_DEFAULT, IPL_NONE);
220 sunxi_hdmi_i2c_init(sc);
221 }
222
223 void
224 sunxi_hdmi_doreset(void)
225 {
226 device_t dev;
227 struct sunxi_hdmi_softc *sc;
228 int error;
229
230 for (int i = 0;;i++) {
231 dev = device_find_by_driver_unit("sunxihdmi", i);
232 if (dev == NULL)
233 return;
234 sc = device_private(dev);
235
236 error = clk_disable(sc->sc_clk_mod);
237 if (error) {
238 aprint_error_dev(dev, ": couldn't disable mod clock\n");
239 return;
240 }
241
242 #if defined(SUNXI_HDMI_DEBUG)
243 sunxi_hdmi_dump_regs();
244 #endif
245
246 /*
247 * reset device, in case it has been setup by firmware in an
248 * incompatible way
249 */
250 for (int j = 0; j <= 0x500; j += 4) {
251 HDMI_WRITE(sc, j, 0);
252 }
253
254 if (clk_disable(sc->sc_clk_ahb) != 0) {
255 aprint_error_dev(dev, ": couldn't disable ahb clock\n");
256 return;
257 }
258 }
259 }
260
261 static void
262 sunxi_hdmi_i2c_init(struct sunxi_hdmi_softc *sc)
263 {
264 struct i2c_controller *ic = &sc->sc_ic;
265
266 mutex_init(&sc->sc_ic_lock, MUTEX_DEFAULT, IPL_NONE);
267
268 ic->ic_cookie = sc;
269 ic->ic_acquire_bus = sunxi_hdmi_i2c_acquire_bus;
270 ic->ic_release_bus = sunxi_hdmi_i2c_release_bus;
271 ic->ic_exec = sunxi_hdmi_i2c_exec;
272 }
273
274 static int
275 sunxi_hdmi_i2c_acquire_bus(void *priv, int flags)
276 {
277 struct sunxi_hdmi_softc *sc = priv;
278
279 if (flags & I2C_F_POLL) {
280 if (!mutex_tryenter(&sc->sc_ic_lock))
281 return EBUSY;
282 } else {
283 mutex_enter(&sc->sc_ic_lock);
284 }
285
286 return 0;
287 }
288
289 static void
290 sunxi_hdmi_i2c_release_bus(void *priv, int flags)
291 {
292 struct sunxi_hdmi_softc *sc = priv;
293
294 mutex_exit(&sc->sc_ic_lock);
295 }
296
297 static int
298 sunxi_hdmi_i2c_exec(void *priv, i2c_op_t op, i2c_addr_t addr,
299 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
300 {
301 struct sunxi_hdmi_softc *sc = priv;
302 uint8_t *pbuf;
303 uint8_t block;
304 int resid;
305 off_t off;
306 int err;
307
308 KASSERT(mutex_owned(&sc->sc_ic_lock));
309 KASSERT(op == I2C_OP_READ_WITH_STOP);
310 KASSERT(addr == DDC_ADDR);
311 KASSERT(cmdlen > 0);
312 KASSERT(buf != NULL);
313
314 err = sunxi_hdmi_i2c_reset(sc, flags);
315 if (err)
316 goto done;
317
318 block = *(const uint8_t *)cmdbuf;
319 off = (block & 1) ? 128 : 0;
320
321 pbuf = buf;
322 resid = len;
323 while (resid > 0) {
324 size_t blklen = uimin(resid, sc->sc_i2c_blklen);
325
326 err = sunxi_hdmi_i2c_xfer(sc, addr, block >> 1, off, blklen,
327 SUNXI_HDMI_DDC_COMMAND_ACCESS_CMD_EOREAD, flags);
328 if (err)
329 goto done;
330
331 if (HDMI_1_3_P(sc)) {
332 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh,
333 SUNXI_HDMI_DDC_FIFO_ACCESS_REG, pbuf, blklen);
334 } else {
335 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh,
336 SUNXI_A31_HDMI_DDC_FIFO_ACCESS_REG, pbuf, blklen);
337 }
338
339 #ifdef SUNXI_HDMI_DEBUG
340 printf("off=%d:", (int)off);
341 for (int i = 0; i < blklen; i++)
342 printf(" %02x", pbuf[i]);
343 printf("\n");
344 #endif
345
346 pbuf += blklen;
347 off += blklen;
348 resid -= blklen;
349 }
350
351 done:
352 return err;
353 }
354
355 static int
356 sunxi_hdmi_i2c_xfer_1_3(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
357 size_t len, int type, int flags)
358 {
359 struct sunxi_hdmi_softc *sc = priv;
360 uint32_t val;
361 int retry;
362
363 val = HDMI_READ(sc, SUNXI_HDMI_DDC_CTRL_REG);
364 val &= ~SUNXI_HDMI_DDC_CTRL_FIFO_DIR;
365 HDMI_WRITE(sc, SUNXI_HDMI_DDC_CTRL_REG, val);
366
367 val |= __SHIFTIN(block, SUNXI_HDMI_DDC_SLAVE_ADDR_0);
368 val |= __SHIFTIN(0x60, SUNXI_HDMI_DDC_SLAVE_ADDR_1);
369 val |= __SHIFTIN(reg, SUNXI_HDMI_DDC_SLAVE_ADDR_2);
370 val |= __SHIFTIN(addr, SUNXI_HDMI_DDC_SLAVE_ADDR_3);
371 HDMI_WRITE(sc, SUNXI_HDMI_DDC_SLAVE_ADDR_REG, val);
372
373 val = HDMI_READ(sc, SUNXI_HDMI_DDC_FIFO_CTRL_REG);
374 val |= SUNXI_HDMI_DDC_FIFO_CTRL_ADDR_CLEAR;
375 HDMI_WRITE(sc, SUNXI_HDMI_DDC_FIFO_CTRL_REG, val);
376
377 HDMI_WRITE(sc, SUNXI_HDMI_DDC_BYTE_COUNTER_REG, len);
378
379 HDMI_WRITE(sc, SUNXI_HDMI_DDC_COMMAND_REG, type);
380
381 val = HDMI_READ(sc, SUNXI_HDMI_DDC_CTRL_REG);
382 val |= SUNXI_HDMI_DDC_CTRL_ACCESS_CMD_START;
383 HDMI_WRITE(sc, SUNXI_HDMI_DDC_CTRL_REG, val);
384
385 retry = 1000;
386 while (--retry > 0) {
387 val = HDMI_READ(sc, SUNXI_HDMI_DDC_CTRL_REG);
388 if ((val & SUNXI_HDMI_DDC_CTRL_ACCESS_CMD_START) == 0)
389 break;
390 delay(1000);
391 }
392 if (retry == 0)
393 return ETIMEDOUT;
394
395 val = HDMI_READ(sc, SUNXI_HDMI_DDC_INT_STATUS_REG);
396 if ((val & SUNXI_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE) == 0) {
397 device_printf(sc->sc_dev, "xfer failed, status=%08x\n", val);
398 return EIO;
399 }
400
401 return 0;
402 }
403
404 static int
405 sunxi_hdmi_i2c_xfer_1_4(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
406 size_t len, int type, int flags)
407 {
408 struct sunxi_hdmi_softc *sc = priv;
409 uint32_t val;
410 int retry;
411
412 val = HDMI_READ(sc, SUNXI_A31_HDMI_DDC_FIFO_CTRL_REG);
413 val |= SUNXI_A31_HDMI_DDC_FIFO_CTRL_RST;
414 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_FIFO_CTRL_REG, val);
415
416 val = __SHIFTIN(block, SUNXI_A31_HDMI_DDC_SLAVE_ADDR_SEG_PTR);
417 val |= __SHIFTIN(0x60, SUNXI_A31_HDMI_DDC_SLAVE_ADDR_DDC_CMD);
418 val |= __SHIFTIN(reg, SUNXI_A31_HDMI_DDC_SLAVE_ADDR_OFF_ADR);
419 val |= __SHIFTIN(addr, SUNXI_A31_HDMI_DDC_SLAVE_ADDR_DEV_ADR);
420 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_SLAVE_ADDR_REG, val);
421
422 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_COMMAND_REG,
423 __SHIFTIN(len, SUNXI_A31_HDMI_DDC_COMMAND_DTC) |
424 __SHIFTIN(type, SUNXI_A31_HDMI_DDC_COMMAND_CMD));
425
426 val = HDMI_READ(sc, SUNXI_A31_HDMI_DDC_CTRL_REG);
427 val |= SUNXI_A31_HDMI_DDC_CTRL_ACCESS_CMD_START;
428 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_CTRL_REG, val);
429
430 retry = 1000;
431 while (--retry > 0) {
432 val = HDMI_READ(sc, SUNXI_A31_HDMI_DDC_CTRL_REG);
433 if ((val & SUNXI_A31_HDMI_DDC_CTRL_ACCESS_CMD_START) == 0)
434 break;
435 if (cold)
436 delay(1000);
437 else
438 kpause("hdmiddc", false, mstohz(10), &sc->sc_ic_lock);
439 }
440 if (retry == 0)
441 return ETIMEDOUT;
442
443 return 0;
444 }
445
446 static int
447 sunxi_hdmi_i2c_xfer(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
448 size_t len, int type, int flags)
449 {
450 struct sunxi_hdmi_softc *sc = priv;
451 int rv;
452
453 if (HDMI_1_3_P(sc)) {
454 rv = sunxi_hdmi_i2c_xfer_1_3(priv, addr, block, reg, len,
455 type, flags);
456 } else {
457 rv = sunxi_hdmi_i2c_xfer_1_4(priv, addr, block, reg, len,
458 type, flags);
459 }
460
461 return rv;
462 }
463
464 static int
465 sunxi_hdmi_i2c_reset(struct sunxi_hdmi_softc *sc, int flags)
466 {
467 uint32_t hpd, ctrl;
468
469 hpd = HDMI_READ(sc, SUNXI_HDMI_HPD_REG);
470 if ((hpd & SUNXI_HDMI_HPD_HOTPLUG_DET) == 0) {
471 device_printf(sc->sc_dev, "no device detected\n");
472 return ENODEV; /* no device plugged in */
473 }
474
475 if (HDMI_1_3_P(sc)) {
476 HDMI_WRITE(sc, SUNXI_HDMI_DDC_FIFO_CTRL_REG, 0);
477 HDMI_WRITE(sc, SUNXI_HDMI_DDC_CTRL_REG,
478 SUNXI_HDMI_DDC_CTRL_EN | SUNXI_HDMI_DDC_CTRL_SW_RST);
479
480 delay(1000);
481
482 ctrl = HDMI_READ(sc, SUNXI_HDMI_DDC_CTRL_REG);
483 if (ctrl & SUNXI_HDMI_DDC_CTRL_SW_RST) {
484 device_printf(sc->sc_dev, "reset failed (1.3)\n");
485 return EBUSY;
486 }
487
488 /* N=5,M=1 */
489 HDMI_WRITE(sc, SUNXI_HDMI_DDC_CLOCK_REG,
490 __SHIFTIN(5, SUNXI_HDMI_DDC_CLOCK_N) |
491 __SHIFTIN(1, SUNXI_HDMI_DDC_CLOCK_M));
492
493 HDMI_WRITE(sc, SUNXI_HDMI_DDC_DBG_REG, 0x300);
494 } else {
495 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_CTRL_REG,
496 SUNXI_A31_HDMI_DDC_CTRL_SW_RST);
497
498 /* N=1,M=12 */
499 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_CLOCK_REG,
500 __SHIFTIN(1, SUNXI_HDMI_DDC_CLOCK_N) |
501 __SHIFTIN(12, SUNXI_HDMI_DDC_CLOCK_M));
502
503 HDMI_WRITE(sc, SUNXI_A31_HDMI_DDC_CTRL_REG,
504 SUNXI_A31_HDMI_DDC_CTRL_SDA_PAD_EN |
505 SUNXI_A31_HDMI_DDC_CTRL_SCL_PAD_EN |
506 SUNXI_A31_HDMI_DDC_CTRL_EN);
507 }
508
509 return 0;
510 }
511
512 static int
513 sunxi_hdmi_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
514 {
515 struct sunxi_hdmi_softc *sc = device_private(dev);
516 struct fdt_endpoint *in_ep, *out_ep;
517 int error;
518
519 /* our input is activated by tcon, we activate our output */
520 if (fdt_endpoint_port_index(ep) != SUNXI_PORT_INPUT) {
521 panic("sunxi_hdmi_ep_activate: port %d",
522 fdt_endpoint_port_index(ep));
523 }
524
525 if (!activate)
526 return EOPNOTSUPP;
527
528 /* check that out other input is not active */
529 switch (fdt_endpoint_index(ep)) {
530 case 0:
531 in_ep = fdt_endpoint_get_from_index(&sc->sc_ports,
532 SUNXI_PORT_INPUT, 1);
533 break;
534 case 1:
535 in_ep = fdt_endpoint_get_from_index(&sc->sc_ports,
536 SUNXI_PORT_INPUT, 0);
537 break;
538 default:
539 in_ep = NULL;
540 panic("sunxi_hdmi_ep_activate: input index %d",
541 fdt_endpoint_index(ep));
542 }
543 if (in_ep != NULL) {
544 if (fdt_endpoint_is_active(in_ep))
545 return EBUSY;
546 }
547 /* only one output */
548 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports,
549 SUNXI_PORT_OUTPUT, 0);
550 if (out_ep == NULL) {
551 aprint_error_dev(dev, "no output endpoint\n");
552 return ENODEV;
553 }
554 error = fdt_endpoint_activate(out_ep, activate);
555 if (error == 0) {
556 sc->sc_in_ep = ep;
557 sc->sc_in_rep = fdt_endpoint_remote(ep);
558 sc->sc_out_ep = out_ep;
559 sunxi_hdmi_do_enable(sc);
560 return 0;
561 }
562 return error;
563 }
564
565 static int
566 sunxi_hdmi_ep_enable(device_t dev, struct fdt_endpoint *ep, bool enable)
567 {
568 struct sunxi_hdmi_softc *sc = device_private(dev);
569 int error;
570
571 if (fdt_endpoint_port_index(ep) == SUNXI_PORT_INPUT) {
572 KASSERT(ep == sc->sc_in_ep);
573 if (sc->sc_thread == NULL) {
574 if (enable) {
575 delay(50000);
576 mutex_enter(&sc->sc_pwr_lock);
577 sunxi_hdmi_hpd(sc);
578 mutex_exit(&sc->sc_pwr_lock);
579 kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
580 sunxi_hdmi_thread, sc, &sc->sc_thread, "%s",
581 device_xname(dev));
582 }
583 return 0;
584 } else {
585 mutex_enter(&sc->sc_pwr_lock);
586 error = sunxi_hdmi_poweron(sc, enable);
587 mutex_exit(&sc->sc_pwr_lock);
588 return error;
589 }
590 }
591 panic("sunxi_hdmi_ep_enable");
592 }
593
594 static void
595 sunxi_hdmi_do_enable(struct sunxi_hdmi_softc *sc)
596 {
597 /* complete attach */
598 struct clk *clk;
599 int error;
600 uint32_t dbg0_reg;
601
602 if (clk_enable(sc->sc_clk_ahb) != 0) {
603 aprint_error_dev(sc->sc_dev, "couldn't enable ahb clock\n");
604 return;
605 }
606 /* assume tcon0 uses pll3, tcon1 uses pll7 */
607 switch(fdt_endpoint_index(sc->sc_in_ep)) {
608 case 0:
609 clk = sc->sc_clk_pll0;
610 dbg0_reg = (0<<21);
611 break;
612 case 1:
613 clk = sc->sc_clk_pll1;
614 dbg0_reg = (1<<21);
615 break;
616 default:
617 panic("sunxi_hdmi pll");
618 }
619 error = clk_set_rate(clk, 270000000);
620 if (error) {
621 clk = clk_get_parent(clk);
622 /* probably because this is pllx2 */
623 error = clk_set_rate(clk, 270000000);
624 }
625 if (error) {
626 aprint_error_dev(sc->sc_dev, ": couldn't init pll clock\n");
627 return;
628 }
629 error = clk_set_parent(sc->sc_clk_mod, clk);
630 if (error) {
631 aprint_error_dev(sc->sc_dev, ": couldn't set mod clock parent\n");
632 return;
633 }
634 error = clk_enable(sc->sc_clk_mod);
635 if (error) {
636 aprint_error_dev(sc->sc_dev, ": couldn't enable mod clock\n");
637 return;
638 }
639 delay(1000);
640
641 HDMI_WRITE(sc, SUNXI_HDMI_CTRL_REG, SUNXI_HDMI_CTRL_MODULE_EN);
642 delay(1000);
643 if (sc->sc_type == HDMI_A10) {
644 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL0_REG, 0xfe800000);
645 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL1_REG, 0x00d8c830);
646 } else if (sc->sc_type == HDMI_A31) {
647 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL0_REG, 0x7e80000f);
648 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL1_REG, 0x01ded030);
649 }
650 HDMI_WRITE(sc, SUNXI_HDMI_PLL_DBG0_REG, dbg0_reg);
651 delay(1000);
652 }
653
654 #define EDID_BLOCK_SIZE 128
655
656 static int
657 sunxi_hdmi_read_edid_block(struct sunxi_hdmi_softc *sc, uint8_t *data,
658 uint8_t block)
659 {
660 i2c_tag_t tag = &sc->sc_ic;
661 uint8_t wbuf[2];
662 int error;
663
664 if ((error = iic_acquire_bus(tag, I2C_F_POLL)) != 0)
665 return error;
666
667 wbuf[0] = block; /* start address */
668
669 error = iic_exec(tag, I2C_OP_READ_WITH_STOP, DDC_ADDR, wbuf, 1,
670 data, EDID_BLOCK_SIZE, I2C_F_POLL);
671 iic_release_bus(tag, I2C_F_POLL);
672 return error;
673 }
674
675 static void
676 sunxi_hdmi_read_edid(struct sunxi_hdmi_softc *sc)
677 {
678 const struct videomode *mode;
679 char *edid;
680 struct edid_info *eip;
681 int retry = 4;
682 u_int display_mode;
683
684 edid = kmem_zalloc(EDID_BLOCK_SIZE, KM_SLEEP);
685 eip = kmem_zalloc(sizeof(struct edid_info), KM_SLEEP);
686
687 while (--retry > 0) {
688 if (!sunxi_hdmi_read_edid_block(sc, edid, 0))
689 break;
690 }
691 if (retry == 0) {
692 device_printf(sc->sc_dev, "failed to read EDID\n");
693 } else {
694 if (edid_parse(edid, eip) != 0) {
695 device_printf(sc->sc_dev, "failed to parse EDID\n");
696 }
697 #ifdef SUNXI_HDMI_DEBUG
698 else {
699 edid_print(eip);
700 }
701 #endif
702 }
703
704 if (sc->sc_display_mode == DISPLAY_MODE_AUTO)
705 display_mode = sunxi_hdmi_get_display_mode(sc, eip);
706 else
707 display_mode = sc->sc_display_mode;
708
709 const char *forced = sc->sc_display_mode == DISPLAY_MODE_AUTO ?
710 "auto-detected" : "forced";
711 device_printf(sc->sc_dev, "%s mode (%s)\n",
712 display_mode == DISPLAY_MODE_HDMI ? "HDMI" : "DVI", forced);
713
714 strlcpy(sc->sc_display_vendor, eip->edid_vendorname,
715 sizeof(sc->sc_display_vendor));
716 strlcpy(sc->sc_display_product, eip->edid_productname,
717 sizeof(sc->sc_display_product));
718 sc->sc_current_display_mode = display_mode;
719
720 mode = eip->edid_preferred_mode;
721 if (mode == NULL)
722 mode = pick_mode_by_ref(640, 480, 60);
723
724 if (mode != NULL) {
725 sunxi_hdmi_video_enable(sc, false);
726 fdt_endpoint_enable(sc->sc_in_ep, false);
727 delay(20000);
728
729 sunxi_tcon1_set_videomode(
730 fdt_endpoint_device(sc->sc_in_rep), mode);
731 sunxi_hdmi_set_videomode(sc, mode, display_mode);
732 sunxi_hdmi_set_audiomode(sc, mode, display_mode);
733 fdt_endpoint_enable(sc->sc_in_ep, true);
734 delay(20000);
735 sunxi_hdmi_video_enable(sc, true);
736 }
737 kmem_free(edid, EDID_BLOCK_SIZE);
738 kmem_free(eip, sizeof(struct edid_info));
739 }
740
741 static u_int
742 sunxi_hdmi_get_display_mode(struct sunxi_hdmi_softc *sc,
743 const struct edid_info *ei)
744 {
745 char *edid;
746 bool found_hdmi = false;
747 unsigned int n, p;
748 edid = kmem_zalloc(EDID_BLOCK_SIZE, KM_SLEEP);
749
750 /*
751 * Scan through extension blocks, looking for a CEA-861-D v3
752 * block. If an HDMI Vendor-Specific Data Block (HDMI VSDB) is
753 * found in that, assume HDMI mode.
754 */
755 for (n = 1; n <= MIN(ei->edid_ext_block_count, 4); n++) {
756 if (sunxi_hdmi_read_edid_block(sc, edid, n)) {
757 #ifdef SUNXI_HDMI_DEBUG
758 device_printf(sc->sc_dev,
759 "Failed to read EDID block %d\n", n);
760 #endif
761 break;
762 }
763
764 #ifdef SUNXI_HDMI_DEBUG
765 device_printf(sc->sc_dev, "EDID block #%d:\n", n);
766 #endif
767
768 const uint8_t tag = edid[0];
769 const uint8_t rev = edid[1];
770 const uint8_t off = edid[2];
771
772 #ifdef SUNXI_HDMI_DEBUG
773 device_printf(sc->sc_dev, " Tag %d, Revision %d, Offset %d\n",
774 tag, rev, off);
775 device_printf(sc->sc_dev, " Flags: 0x%02x\n", edid[3]);
776 #endif
777
778 /* We are looking for a CEA-861-D tag (02h) with revision 3 */
779 if (tag != 0x02 || rev != 3)
780 continue;
781 /*
782 * CEA data block collection starts at byte 4, so the
783 * DTD blocks must start after it.
784 */
785 if (off <= 4)
786 continue;
787
788 /* Parse the CEA data blocks */
789 for (p = 4; p < off;) {
790 const uint8_t btag = (edid[p] >> 5) & 0x7;
791 const uint8_t blen = edid[p] & 0x1f;
792
793 #ifdef SUNXI_HDMI_DEBUG
794 device_printf(sc->sc_dev, " CEA data block @ %d\n", p);
795 device_printf(sc->sc_dev, " Tag %d, Length %d\n",
796 btag, blen);
797 #endif
798
799 /* Make sure the length is sane */
800 if (p + blen + 1 > off)
801 break;
802 /* Looking for a VSDB tag */
803 if (btag != 3)
804 goto next_block;
805 /* HDMI VSDB is at least 5 bytes long */
806 if (blen < 5)
807 goto next_block;
808
809 #ifdef SUNXI_HDMI_DEBUG
810 device_printf(sc->sc_dev, " ID: %02x%02x%02x\n",
811 edid[p + 1], edid[p + 2], edid[p + 3]);
812 #endif
813
814 /* HDMI 24-bit IEEE registration ID is 0x000C03 */
815 if (memcmp(&edid[p + 1], "\x03\x0c\x00", 3) == 0)
816 found_hdmi = true;
817
818 next_block:
819 p += (1 + blen);
820 }
821 }
822
823 kmem_free(edid, EDID_BLOCK_SIZE);
824 return found_hdmi ? DISPLAY_MODE_HDMI : DISPLAY_MODE_DVI;
825 }
826
827 static void
828 sunxi_hdmi_video_enable(struct sunxi_hdmi_softc *sc, bool enable)
829 {
830 uint32_t val;
831
832 fdt_endpoint_enable(sc->sc_out_ep, enable);
833
834 val = HDMI_READ(sc, SUNXI_HDMI_VID_CTRL_REG);
835 val &= ~SUNXI_HDMI_VID_CTRL_SRC_SEL;
836 #ifdef SUNXI_HDMI_CBGEN
837 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_SRC_SEL_CBGEN,
838 SUNXI_HDMI_VID_CTRL_SRC_SEL);
839 #else
840 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_SRC_SEL_RGB,
841 SUNXI_HDMI_VID_CTRL_SRC_SEL);
842 #endif
843 if (enable) {
844 val |= SUNXI_HDMI_VID_CTRL_VIDEO_EN;
845 } else {
846 val &= ~SUNXI_HDMI_VID_CTRL_VIDEO_EN;
847 }
848 HDMI_WRITE(sc, SUNXI_HDMI_VID_CTRL_REG, val);
849
850 #if defined(SUNXI_HDMI_DEBUG)
851 sunxi_hdmi_dump_regs();
852 #endif
853 }
854
855 static void
856 sunxi_hdmi_set_videomode(struct sunxi_hdmi_softc *sc,
857 const struct videomode *mode, u_int display_mode)
858 {
859 uint32_t val;
860 const u_int dblscan_p = !!(mode->flags & VID_DBLSCAN);
861 const u_int interlace_p = !!(mode->flags & VID_INTERLACE);
862 const u_int phsync_p = !!(mode->flags & VID_PHSYNC);
863 const u_int pvsync_p = !!(mode->flags & VID_PVSYNC);
864 const u_int hfp = mode->hsync_start - mode->hdisplay;
865 const u_int hspw = mode->hsync_end - mode->hsync_start;
866 const u_int hbp = mode->htotal - mode->hsync_start;
867 const u_int vfp = mode->vsync_start - mode->vdisplay;
868 const u_int vspw = mode->vsync_end - mode->vsync_start;
869 const u_int vbp = mode->vtotal - mode->vsync_start;
870 struct clk *clk_pll;
871 int parent_rate;
872 int best_div, best_dbl, best_diff;
873 int target_rate = mode->dot_clock * 1000;
874
875 #ifdef SUNXI_HDMI_DEBUG
876 device_printf(sc->sc_dev,
877 "dblscan %d, interlace %d, phsync %d, pvsync %d\n",
878 dblscan_p, interlace_p, phsync_p, pvsync_p);
879 device_printf(sc->sc_dev, "h: %u %u %u %u\n",
880 mode->hdisplay, hbp, hfp, hspw);
881 device_printf(sc->sc_dev, "v: %u %u %u %u\n",
882 mode->vdisplay, vbp, vfp, vspw);
883 #endif
884
885 HDMI_WRITE(sc, SUNXI_HDMI_INT_STATUS_REG, 0xffffffff);
886
887 /* assume tcon0 uses pll3, tcon1 uses pll7 */
888 switch(fdt_endpoint_index(sc->sc_in_ep)) {
889 case 0:
890 clk_pll = sc->sc_clk_pll0;
891 break;
892 case 1:
893 clk_pll = sc->sc_clk_pll1;
894 break;
895 default:
896 panic("sunxi_hdmi pll");
897 }
898 parent_rate = clk_get_rate(clk_pll);
899 KASSERT(parent_rate > 0);
900 best_div = best_dbl = 0;
901 best_diff = INT_MAX;
902 for (int d = 2; d > 0 && best_diff != 0; d--) {
903 for (int m = 1; m <= 16 && best_diff != 0; m++) {
904 int cur_rate = parent_rate / m / d;
905 int diff = abs(target_rate - cur_rate);
906 if (diff >= 0 && diff < best_diff) {
907 best_diff = diff;
908 best_div = m;
909 best_dbl = d;
910 }
911 }
912 }
913
914 #ifdef SUNXI_HDMI_DEBUG
915 device_printf(sc->sc_dev, "parent rate: %d\n", parent_rate);
916 device_printf(sc->sc_dev, "dot_clock: %d\n", mode->dot_clock);
917 device_printf(sc->sc_dev, "clkdiv: %d\n", best_div);
918 device_printf(sc->sc_dev, "clkdbl: %c\n", (best_dbl == 1) ? 'Y' : 'N');
919 #endif
920
921 if (best_div == 0) {
922 device_printf(sc->sc_dev, "ERROR: TCON clk not configured\n");
923 return;
924 }
925
926 uint32_t pll_ctrl, pad_ctrl0, pad_ctrl1;
927 if (HDMI_1_4_P(sc)) {
928 pad_ctrl0 = 0x7e8000ff;
929 pad_ctrl1 = 0x01ded030;
930 pll_ctrl = 0xba48a308;
931 pll_ctrl |= __SHIFTIN(best_div - 1, SUNXI_HDMI_PLL_CTRL_PREDIV);
932 } else {
933 pad_ctrl0 = 0xfe800000;
934 pad_ctrl1 = 0x00d8c830;
935 pll_ctrl = 0xfa4ef708;
936 pll_ctrl |= __SHIFTIN(best_div, SUNXI_HDMI_PLL_CTRL_PREDIV);
937 }
938 if (best_dbl == 2)
939 pad_ctrl1 |= 0x40;
940
941 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL0_REG, pad_ctrl0);
942 HDMI_WRITE(sc, SUNXI_HDMI_PAD_CTRL1_REG, pad_ctrl1);
943 HDMI_WRITE(sc, SUNXI_HDMI_PLL_CTRL_REG, pll_ctrl);
944 /* assume tcon0 uses pll3, tcon1 uses pll7 */
945 switch(fdt_endpoint_index(sc->sc_in_ep)) {
946 case 0:
947 HDMI_WRITE(sc, SUNXI_HDMI_PLL_DBG0_REG, (0<<21));
948 break;
949 case 1:
950 HDMI_WRITE(sc, SUNXI_HDMI_PLL_DBG0_REG, (1<<21));
951 break;
952 default:
953 panic("sunxi_hdmi pll");
954 }
955
956 val = HDMI_READ(sc, SUNXI_HDMI_VID_CTRL_REG);
957 val &= ~SUNXI_HDMI_VID_CTRL_HDMI_MODE;
958 if (display_mode == DISPLAY_MODE_DVI) {
959 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_HDMI_MODE_DVI,
960 SUNXI_HDMI_VID_CTRL_HDMI_MODE);
961 } else {
962 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_HDMI_MODE_HDMI,
963 SUNXI_HDMI_VID_CTRL_HDMI_MODE);
964 }
965 val &= ~SUNXI_HDMI_VID_CTRL_REPEATER_SEL;
966 if (dblscan_p) {
967 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_REPEATER_SEL_2X,
968 SUNXI_HDMI_VID_CTRL_REPEATER_SEL);
969 }
970 val &= ~SUNXI_HDMI_VID_CTRL_OUTPUT_FMT;
971 if (interlace_p) {
972 val |= __SHIFTIN(SUNXI_HDMI_VID_CTRL_OUTPUT_FMT_INTERLACE,
973 SUNXI_HDMI_VID_CTRL_OUTPUT_FMT);
974 }
975 HDMI_WRITE(sc, SUNXI_HDMI_VID_CTRL_REG, val);
976
977 val = __SHIFTIN((mode->hdisplay << dblscan_p) - 1,
978 SUNXI_HDMI_VID_TIMING_0_ACT_H);
979 val |= __SHIFTIN(mode->vdisplay - 1,
980 SUNXI_HDMI_VID_TIMING_0_ACT_V);
981 HDMI_WRITE(sc, SUNXI_HDMI_VID_TIMING_0_REG, val);
982
983 val = __SHIFTIN((hbp << dblscan_p) - 1,
984 SUNXI_HDMI_VID_TIMING_1_HBP);
985 val |= __SHIFTIN(vbp - 1,
986 SUNXI_HDMI_VID_TIMING_1_VBP);
987 HDMI_WRITE(sc, SUNXI_HDMI_VID_TIMING_1_REG, val);
988
989 val = __SHIFTIN((hfp << dblscan_p) - 1,
990 SUNXI_HDMI_VID_TIMING_2_HFP);
991 val |= __SHIFTIN(vfp - 1,
992 SUNXI_HDMI_VID_TIMING_2_VFP);
993 HDMI_WRITE(sc, SUNXI_HDMI_VID_TIMING_2_REG, val);
994
995 val = __SHIFTIN((hspw << dblscan_p) - 1,
996 SUNXI_HDMI_VID_TIMING_3_HSPW);
997 val |= __SHIFTIN(vspw - 1,
998 SUNXI_HDMI_VID_TIMING_3_VSPW);
999 HDMI_WRITE(sc, SUNXI_HDMI_VID_TIMING_3_REG, val);
1000
1001 val = 0;
1002 if (phsync_p) {
1003 val |= SUNXI_HDMI_VID_TIMING_4_HSYNC_ACTIVE_SEL;
1004 }
1005 if (pvsync_p) {
1006 val |= SUNXI_HDMI_VID_TIMING_4_VSYNC_ACTIVE_SEL;
1007 }
1008 val |= __SHIFTIN(SUNXI_HDMI_VID_TIMING_4_TX_CLOCK_NORMAL,
1009 SUNXI_HDMI_VID_TIMING_4_TX_CLOCK);
1010 HDMI_WRITE(sc, SUNXI_HDMI_VID_TIMING_4_REG, val);
1011
1012 /* Packet control */
1013 HDMI_WRITE(sc, SUNXI_HDMI_GP_PKT0_REG, 0);
1014 HDMI_WRITE(sc, SUNXI_HDMI_GP_PKT1_REG, 0);
1015 HDMI_WRITE(sc, SUNXI_HDMI_PKT_CTRL0_REG, 0x00005321);
1016 HDMI_WRITE(sc, SUNXI_HDMI_PKT_CTRL1_REG, 0x0000000f);
1017 }
1018
1019 static void
1020 sunxi_hdmi_set_audiomode(struct sunxi_hdmi_softc *sc,
1021 const struct videomode *mode, u_int display_mode)
1022 {
1023 uint32_t cts, n, val;
1024
1025 /*
1026 * Before changing audio parameters, disable and reset the
1027 * audio module. Wait for the soft reset bit to clear before
1028 * configuring the audio parameters.
1029 */
1030 val = HDMI_READ(sc, SUNXI_HDMI_AUD_CTRL_REG);
1031 val &= ~SUNXI_HDMI_AUD_CTRL_EN;
1032 val |= SUNXI_HDMI_AUD_CTRL_RST;
1033 HDMI_WRITE(sc, SUNXI_HDMI_AUD_CTRL_REG, val);
1034 do {
1035 val = HDMI_READ(sc, SUNXI_HDMI_AUD_CTRL_REG);
1036 } while (val & SUNXI_HDMI_AUD_CTRL_RST);
1037
1038 /* No audio support in DVI mode */
1039 if (display_mode != DISPLAY_MODE_HDMI) {
1040 return;
1041 }
1042
1043 /* DMA & FIFO control */
1044 val = HDMI_READ(sc, SUNXI_HDMI_ADMA_CTRL_REG);
1045 if (sc->sc_type == HDMI_A31) {
1046 val |= SUNXI_HDMI_ADMA_CTRL_SRC_DMA_MODE; /* NDMA */
1047 } else {
1048 val &= ~SUNXI_HDMI_ADMA_CTRL_SRC_DMA_MODE; /* DDMA */
1049 }
1050 val &= ~SUNXI_HDMI_ADMA_CTRL_SRC_DMA_SAMPLE_RATE;
1051 val &= ~SUNXI_HDMI_ADMA_CTRL_SRC_SAMPLE_LAYOUT;
1052 val &= ~SUNXI_HDMI_ADMA_CTRL_SRC_WORD_LEN;
1053 val &= ~SUNXI_HDMI_ADMA_CTRL_DATA_SEL;
1054 HDMI_WRITE(sc, SUNXI_HDMI_ADMA_CTRL_REG, val);
1055
1056 /* Audio format control */
1057 val = HDMI_READ(sc, SUNXI_HDMI_AUD_FMT_REG);
1058 val &= ~SUNXI_HDMI_AUD_FMT_SRC_SEL;
1059 val &= ~SUNXI_HDMI_AUD_FMT_SEL;
1060 val &= ~SUNXI_HDMI_AUD_FMT_DSD_FMT;
1061 val &= ~SUNXI_HDMI_AUD_FMT_LAYOUT;
1062 val &= ~SUNXI_HDMI_AUD_FMT_SRC_CH_CFG;
1063 val |= __SHIFTIN(1, SUNXI_HDMI_AUD_FMT_SRC_CH_CFG);
1064 HDMI_WRITE(sc, SUNXI_HDMI_AUD_FMT_REG, val);
1065
1066 /* PCM control (channel map) */
1067 HDMI_WRITE(sc, SUNXI_HDMI_AUD_PCM_CTRL_REG, 0x76543210);
1068
1069 /* Clock setup */
1070 n = 6144; /* 48 kHz */
1071 cts = ((mode->dot_clock * 10) * (n / 128)) / 480;
1072 HDMI_WRITE(sc, SUNXI_HDMI_AUD_CTS_REG, cts);
1073 HDMI_WRITE(sc, SUNXI_HDMI_AUD_N_REG, n);
1074
1075 /* Audio PCM channel status 0 */
1076 val = __SHIFTIN(SUNXI_HDMI_AUD_CH_STATUS0_FS_FREQ_48,
1077 SUNXI_HDMI_AUD_CH_STATUS0_FS_FREQ);
1078 HDMI_WRITE(sc, SUNXI_HDMI_AUD_CH_STATUS0_REG, val);
1079
1080 /* Audio PCM channel status 1 */
1081 val = HDMI_READ(sc, SUNXI_HDMI_AUD_CH_STATUS1_REG);
1082 val &= ~SUNXI_HDMI_AUD_CH_STATUS1_CGMS_A;
1083 val &= ~SUNXI_HDMI_AUD_CH_STATUS1_ORIGINAL_FS;
1084 val &= ~SUNXI_HDMI_AUD_CH_STATUS1_WORD_LEN;
1085 val |= __SHIFTIN(5, SUNXI_HDMI_AUD_CH_STATUS1_WORD_LEN);
1086 val |= SUNXI_HDMI_AUD_CH_STATUS1_WORD_LEN_MAX;
1087 HDMI_WRITE(sc, SUNXI_HDMI_AUD_CH_STATUS1_REG, val);
1088
1089 /* Re-enable */
1090 val = HDMI_READ(sc, SUNXI_HDMI_AUD_CTRL_REG);
1091 val |= SUNXI_HDMI_AUD_CTRL_EN;
1092 HDMI_WRITE(sc, SUNXI_HDMI_AUD_CTRL_REG, val);
1093
1094 #if defined(SUNXI_HDMI_DEBUG)
1095 sunxi_hdmi_dump_regs();
1096 #endif
1097 }
1098
1099 static void
1100 sunxi_hdmi_hpd(struct sunxi_hdmi_softc *sc)
1101 {
1102 uint32_t hpd = HDMI_READ(sc, SUNXI_HDMI_HPD_REG);
1103 bool con = !!(hpd & SUNXI_HDMI_HPD_HOTPLUG_DET);
1104
1105 KASSERT(mutex_owned(&sc->sc_pwr_lock));
1106 if (sc->sc_display_connected == con)
1107 return;
1108
1109 if (con) {
1110 device_printf(sc->sc_dev, "display connected\n");
1111 sc->sc_pwr_refcount = 1;
1112 sunxi_hdmi_read_edid(sc);
1113 } else {
1114 device_printf(sc->sc_dev, "display disconnected\n");
1115 sc->sc_pwr_refcount = 0;
1116 sunxi_hdmi_video_enable(sc, false);
1117 fdt_endpoint_enable(sc->sc_in_ep, false);
1118 sunxi_tcon1_set_videomode(
1119 fdt_endpoint_device(sc->sc_in_rep), NULL);
1120 }
1121
1122 sc->sc_display_connected = con;
1123 }
1124
1125 static void
1126 sunxi_hdmi_thread(void *priv)
1127 {
1128 struct sunxi_hdmi_softc *sc = priv;
1129
1130 for (;;) {
1131 mutex_enter(&sc->sc_pwr_lock);
1132 sunxi_hdmi_hpd(sc);
1133 mutex_exit(&sc->sc_pwr_lock);
1134 kpause("hdmihotplug", false, mstohz(1000), NULL);
1135 }
1136 }
1137
1138 static int
1139 sunxi_hdmi_poweron(struct sunxi_hdmi_softc *sc, bool enable)
1140 {
1141 int error = 0;
1142 KASSERT(mutex_owned(&sc->sc_pwr_lock));
1143 if (!sc->sc_display_connected)
1144 return EOPNOTSUPP;
1145 if (enable) {
1146 KASSERT(sc->sc_pwr_refcount >= 0);
1147 if (sc->sc_pwr_refcount == 0) {
1148 error = fdt_endpoint_enable(sc->sc_in_ep, true);
1149 if (error)
1150 return error;
1151 sunxi_hdmi_video_enable(sc, true);
1152 }
1153 sc->sc_pwr_refcount++;
1154 } else {
1155 sc->sc_pwr_refcount--;
1156 KASSERT(sc->sc_pwr_refcount >= 0);
1157 if (sc->sc_pwr_refcount == 0) {
1158 sunxi_hdmi_video_enable(sc, false);
1159 error = fdt_endpoint_enable(sc->sc_in_ep, false);
1160 }
1161 }
1162 return error;
1163 }
1164 #if 0
1165 static int
1166 sunxi_hdmi_intr(void *priv)
1167 {
1168 struct sunxi_hdmi_softc *sc = priv;
1169 uint32_t intsts;
1170
1171 intsts = HDMI_READ(sc, SUNXI_HDMI_INT_STATUS_REG);
1172 if (!(intsts & 0x73))
1173 return 0;
1174
1175 HDMI_WRITE(sc, SUNXI_HDMI_INT_STATUS_REG, intsts);
1176
1177 device_printf(sc->sc_dev, "INT_STATUS %08X\n", intsts);
1178
1179 return 1;
1180 }
1181 #endif
1182
1183 #if 0 /* XXX audio */
1184 void
1185 sunxi_hdmi_get_info(struct sunxi_hdmi_info *info)
1186 {
1187 struct sunxi_hdmi_softc *sc;
1188 device_t dev;
1189
1190 memset(info, 0, sizeof(*info));
1191
1192 dev = device_find_by_driver_unit("sunxihdmi", 0);
1193 if (dev == NULL) {
1194 info->display_connected = false;
1195 return;
1196 }
1197 sc = device_private(dev);
1198
1199 info->display_connected = sc->sc_display_connected;
1200 if (info->display_connected) {
1201 strlcpy(info->display_vendor, sc->sc_display_vendor,
1202 sizeof(info->display_vendor));
1203 strlcpy(info->display_product, sc->sc_display_product,
1204 sizeof(info->display_product));
1205 info->display_hdmimode =
1206 sc->sc_current_display_mode == DISPLAY_MODE_HDMI;
1207 }
1208 }
1209 #endif
1210
1211 #if defined(SUNXI_HDMI_DEBUG)
1212 void
1213 sunxi_hdmi_dump_regs(void)
1214 {
1215 static const struct {
1216 const char *name;
1217 uint16_t reg;
1218 } regs[] = {
1219 { "CTRL", SUNXI_HDMI_CTRL_REG },
1220 { "INT_STATUS", SUNXI_HDMI_INT_STATUS_REG },
1221 { "VID_CTRL", SUNXI_HDMI_VID_CTRL_REG },
1222 { "VID_TIMING_0", SUNXI_HDMI_VID_TIMING_0_REG },
1223 { "VID_TIMING_1", SUNXI_HDMI_VID_TIMING_1_REG },
1224 { "VID_TIMING_2", SUNXI_HDMI_VID_TIMING_2_REG },
1225 { "VID_TIMING_3", SUNXI_HDMI_VID_TIMING_3_REG },
1226 { "VID_TIMING_4", SUNXI_HDMI_VID_TIMING_4_REG },
1227 { "PAD_CTRL0", SUNXI_HDMI_PAD_CTRL0_REG },
1228 { "PAD_CTRL1", SUNXI_HDMI_PAD_CTRL1_REG },
1229 { "PLL_CTRL", SUNXI_HDMI_PLL_CTRL_REG },
1230 { "PLL_DBG0", SUNXI_HDMI_PLL_DBG0_REG },
1231 { "PLL_DBG1", SUNXI_HDMI_PLL_DBG1_REG },
1232 };
1233 struct sunxi_hdmi_softc *sc;
1234 device_t dev;
1235
1236 dev = device_find_by_driver_unit("sunxihdmi", 0);
1237 if (dev == NULL)
1238 return;
1239 sc = device_private(dev);
1240
1241 for (int i = 0; i < __arraycount(regs); i++) {
1242 printf("%s: 0x%08x\n", regs[i].name,
1243 HDMI_READ(sc, regs[i].reg));
1244 }
1245 }
1246 #endif
1247