xref: /freebsd/sys/arm/freescale/imx/imx6_hdmi.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
1 /*-
2  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /*
31  * HDMI core module
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #include <machine/bus.h>
45 
46 #include <dev/videomode/videomode.h>
47 #include <dev/videomode/edidvar.h>
48 
49 #include <dev/iicbus/iicbus.h>
50 #include <dev/iicbus/iiconf.h>
51 
52 #include <arm/freescale/imx/imx_ccmvar.h>
53 #include <arm/freescale/imx/imx_iomuxvar.h>
54 #include <arm/freescale/imx/imx_iomuxreg.h>
55 #include <arm/freescale/imx/imx6_hdmireg.h>
56 
57 #include "hdmi_if.h"
58 
59 #define	I2C_DDC_ADDR	(0x50 << 1)
60 #define	EDID_LENGTH	0x80
61 
62 struct imx_hdmi_softc {
63 	device_t		sc_dev;
64 	struct resource		*sc_mem_res;
65 	int			sc_mem_rid;
66 	struct intr_config_hook	sc_mode_hook;
67 	struct videomode	sc_mode;
68 	uint8_t			*sc_edid;
69 	uint8_t			sc_edid_len;
70 	phandle_t		sc_i2c_xref;
71 };
72 
73 static struct ofw_compat_data compat_data[] = {
74 	{"fsl,imx6dl-hdmi", 1},
75 	{"fsl,imx6q-hdmi",  1},
76 	{NULL,	            0}
77 };
78 
79 static inline uint8_t
80 RD1(struct imx_hdmi_softc *sc, bus_size_t off)
81 {
82 
83 	return (bus_read_1(sc->sc_mem_res, off));
84 }
85 
86 static inline void
87 WR1(struct imx_hdmi_softc *sc, bus_size_t off, uint8_t val)
88 {
89 
90 	bus_write_1(sc->sc_mem_res, off, val);
91 }
92 
93 static void
94 imx_hdmi_phy_wait_i2c_done(struct imx_hdmi_softc *sc, int msec)
95 {
96 	uint8_t val;
97 
98 	val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
99 	    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
100 	while (val == 0) {
101 		pause("HDMI_PHY", hz/100);
102 		msec -= 10;
103 		if (msec <= 0)
104 			return;
105 		val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
106 		    (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
107 	}
108 }
109 
110 static void
111 imx_hdmi_phy_i2c_write(struct imx_hdmi_softc *sc, unsigned short data,
112     unsigned char addr)
113 {
114 
115 	/* clear DONE and ERROR flags */
116 	WR1(sc, HDMI_IH_I2CMPHY_STAT0,
117 	    HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
118 	WR1(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
119 	WR1(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff));
120 	WR1(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff));
121 	WR1(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
122 	imx_hdmi_phy_wait_i2c_done(sc, 1000);
123 }
124 
125 static void
126 imx_hdmi_disable_overflow_interrupts(struct imx_hdmi_softc *sc)
127 {
128 	WR1(sc, HDMI_IH_MUTE_FC_STAT2, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK);
129 	WR1(sc, HDMI_FC_MASK2,
130 	    HDMI_FC_MASK2_LOW_PRI | HDMI_FC_MASK2_HIGH_PRI);
131 }
132 
133 static void
134 imx_hdmi_av_composer(struct imx_hdmi_softc *sc)
135 {
136 	uint8_t inv_val;
137 	int is_dvi;
138 	int hblank, vblank, hsync_len, hbp, vbp;
139 
140 	/* Set up HDMI_FC_INVIDCONF */
141 	inv_val = ((sc->sc_mode.flags & VID_NVSYNC) ?
142 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW :
143 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH);
144 
145 	inv_val |= ((sc->sc_mode.flags & VID_NHSYNC) ?
146 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW :
147 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH);
148 
149 	inv_val |= HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH;
150 
151 	inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
152 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
153 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
154 
155 	inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
156 		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
157 		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
158 
159 	/* TODO: implement HDMI part */
160 	is_dvi = 1;
161 	inv_val |= (is_dvi ?
162 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
163 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
164 
165 	WR1(sc, HDMI_FC_INVIDCONF, inv_val);
166 
167 	/* Set up horizontal active pixel region width */
168 	WR1(sc, HDMI_FC_INHACTV1, sc->sc_mode.hdisplay >> 8);
169 	WR1(sc, HDMI_FC_INHACTV0, sc->sc_mode.hdisplay);
170 
171 	/* Set up vertical blanking pixel region width */
172 	WR1(sc, HDMI_FC_INVACTV1, sc->sc_mode.vdisplay >> 8);
173 	WR1(sc, HDMI_FC_INVACTV0, sc->sc_mode.vdisplay);
174 
175 	/* Set up horizontal blanking pixel region width */
176 	hblank = sc->sc_mode.htotal - sc->sc_mode.hdisplay;
177 	WR1(sc, HDMI_FC_INHBLANK1, hblank >> 8);
178 	WR1(sc, HDMI_FC_INHBLANK0, hblank);
179 
180 	/* Set up vertical blanking pixel region width */
181 	vblank = sc->sc_mode.vtotal - sc->sc_mode.vdisplay;
182 	WR1(sc, HDMI_FC_INVBLANK, vblank);
183 
184 	/* Set up HSYNC active edge delay width (in pixel clks) */
185 	hbp = sc->sc_mode.htotal - sc->sc_mode.hsync_end;
186 	WR1(sc, HDMI_FC_HSYNCINDELAY1, hbp >> 8);
187 	WR1(sc, HDMI_FC_HSYNCINDELAY0, hbp);
188 
189 	/* Set up VSYNC active edge delay (in pixel clks) */
190 	vbp = sc->sc_mode.vtotal - sc->sc_mode.vsync_end;
191 	WR1(sc, HDMI_FC_VSYNCINDELAY, vbp);
192 
193 	hsync_len = (sc->sc_mode.hsync_end - sc->sc_mode.hsync_start);
194 	/* Set up HSYNC active pulse width (in pixel clks) */
195 	WR1(sc, HDMI_FC_HSYNCINWIDTH1, hsync_len >> 8);
196 	WR1(sc, HDMI_FC_HSYNCINWIDTH0, hsync_len);
197 
198 	/* Set up VSYNC active edge delay (in pixel clks) */
199 	WR1(sc, HDMI_FC_VSYNCINWIDTH, (sc->sc_mode.vsync_end - sc->sc_mode.vsync_start));
200 }
201 
202 static void
203 imx_hdmi_phy_enable_power(struct imx_hdmi_softc *sc, uint8_t enable)
204 {
205 	uint8_t reg;
206 
207 	reg = RD1(sc, HDMI_PHY_CONF0);
208 	reg &= ~HDMI_PHY_CONF0_PDZ_MASK;
209 	reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET);
210 	WR1(sc, HDMI_PHY_CONF0, reg);
211 }
212 
213 static void
214 imx_hdmi_phy_enable_tmds(struct imx_hdmi_softc *sc, uint8_t enable)
215 {
216 	uint8_t reg;
217 
218 	reg = RD1(sc, HDMI_PHY_CONF0);
219 	reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK;
220 	reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
221 	WR1(sc, HDMI_PHY_CONF0, reg);
222 }
223 
224 static void
225 imx_hdmi_phy_gen2_pddq(struct imx_hdmi_softc *sc, uint8_t enable)
226 {
227 	uint8_t reg;
228 
229 	reg = RD1(sc, HDMI_PHY_CONF0);
230 	reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
231 	reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
232 	WR1(sc, HDMI_PHY_CONF0, reg);
233 }
234 
235 static void
236 imx_hdmi_phy_gen2_txpwron(struct imx_hdmi_softc *sc, uint8_t enable)
237 {
238 	uint8_t reg;
239 
240 	reg = RD1(sc, HDMI_PHY_CONF0);
241 	reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
242 	reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
243 	WR1(sc, HDMI_PHY_CONF0, reg);
244 }
245 
246 static void
247 imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi_softc *sc, uint8_t enable)
248 {
249 	uint8_t reg;
250 
251 	reg = RD1(sc, HDMI_PHY_CONF0);
252 	reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK;
253 	reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
254 	WR1(sc, HDMI_PHY_CONF0, reg);
255 }
256 
257 static void
258 imx_hdmi_phy_sel_interface_control(struct imx_hdmi_softc *sc, uint8_t enable)
259 {
260 	uint8_t reg;
261 
262 	reg = RD1(sc, HDMI_PHY_CONF0);
263 	reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK;
264 	reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
265 	WR1(sc, HDMI_PHY_CONF0, reg);
266 }
267 
268 static inline void
269 imx_hdmi_phy_test_clear(struct imx_hdmi_softc *sc, unsigned char bit)
270 {
271 	uint8_t val;
272 
273 	val = RD1(sc, HDMI_PHY_TST0);
274 	val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
275 	val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
276 		HDMI_PHY_TST0_TSTCLR_MASK;
277 	WR1(sc, HDMI_PHY_TST0, val);
278 }
279 
280 static void
281 imx_hdmi_clear_overflow(struct imx_hdmi_softc *sc)
282 {
283 	int count;
284 	uint8_t val;
285 
286 	/* TMDS software reset */
287 	WR1(sc, HDMI_MC_SWRSTZ, (uint8_t)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ);
288 
289 	val = RD1(sc, HDMI_FC_INVIDCONF);
290 
291 	for (count = 0 ; count < 5 ; count++)
292 		WR1(sc, HDMI_FC_INVIDCONF, val);
293 }
294 
295 static int
296 imx_hdmi_phy_configure(struct imx_hdmi_softc *sc)
297 {
298 	uint8_t val;
299 	uint8_t msec;
300 
301 	WR1(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS);
302 
303 	/* gen2 tx power off */
304 	imx_hdmi_phy_gen2_txpwron(sc, 0);
305 
306 	/* gen2 pddq */
307 	imx_hdmi_phy_gen2_pddq(sc, 1);
308 
309 	/* PHY reset */
310 	WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
311 	WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT);
312 
313 	WR1(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT);
314 
315 	imx_hdmi_phy_test_clear(sc, 1);
316 	WR1(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
317 	imx_hdmi_phy_test_clear(sc, 0);
318 
319 	/*
320 	 * Following initialization are for 8bit per color case
321 	 */
322 
323 	/*
324 	 * PLL/MPLL config, see section 24.7.22 in TRM
325 	 *  config, see section 24.7.22
326 	 */
327 	if (sc->sc_mode.dot_clock*1000 <= 45250000) {
328 		imx_hdmi_phy_i2c_write(sc, CPCE_CTRL_45_25, HDMI_PHY_I2C_CPCE_CTRL);
329 		imx_hdmi_phy_i2c_write(sc, GMPCTRL_45_25, HDMI_PHY_I2C_GMPCTRL);
330 	} else if (sc->sc_mode.dot_clock*1000 <= 92500000) {
331 		imx_hdmi_phy_i2c_write(sc, CPCE_CTRL_92_50, HDMI_PHY_I2C_CPCE_CTRL);
332 		imx_hdmi_phy_i2c_write(sc, GMPCTRL_92_50, HDMI_PHY_I2C_GMPCTRL);
333 	} else if (sc->sc_mode.dot_clock*1000 <= 185000000) {
334 		imx_hdmi_phy_i2c_write(sc, CPCE_CTRL_185, HDMI_PHY_I2C_CPCE_CTRL);
335 		imx_hdmi_phy_i2c_write(sc, GMPCTRL_185, HDMI_PHY_I2C_GMPCTRL);
336 	} else {
337 		imx_hdmi_phy_i2c_write(sc, CPCE_CTRL_370, HDMI_PHY_I2C_CPCE_CTRL);
338 		imx_hdmi_phy_i2c_write(sc, GMPCTRL_370, HDMI_PHY_I2C_GMPCTRL);
339 	}
340 
341 	/*
342 	 * Values described in TRM section 34.9.2 PLL/MPLL Generic
343 	 *    Configuration Settings. Table 34-23.
344 	 */
345 	if (sc->sc_mode.dot_clock*1000 <= 54000000) {
346 		imx_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
347 	} else if (sc->sc_mode.dot_clock*1000 <= 58400000) {
348 		imx_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
349 	} else if (sc->sc_mode.dot_clock*1000 <= 72000000) {
350 		imx_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
351 	} else if (sc->sc_mode.dot_clock*1000 <= 74250000) {
352 		imx_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
353 	} else if (sc->sc_mode.dot_clock*1000 <= 118800000) {
354 		imx_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
355 	} else if (sc->sc_mode.dot_clock*1000 <= 216000000) {
356 		imx_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
357 	} else {
358 		panic("Unsupported mode\n");
359 	}
360 
361 	imx_hdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL);
362 	imx_hdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL);
363 	/* RESISTANCE TERM 133 Ohm */
364 	imx_hdmi_phy_i2c_write(sc, TXTERM_133, HDMI_PHY_I2C_TXTERM);
365 
366 	/* REMOVE CLK TERM */
367 	imx_hdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL);
368 
369 	if (sc->sc_mode.dot_clock*1000 > 148500000) {
370 		imx_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
371 		    CKSYMTXCTRL_TX_TRBON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL);
372 		imx_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(9) | VLEVCTRL_CK_LVL(9),
373 		    HDMI_PHY_I2C_VLEVCTRL);
374 	} else {
375 		imx_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
376 		    CKSYMTXCTRL_TX_TRAON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL);
377 		imx_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(13) | VLEVCTRL_CK_LVL(13),
378 		    HDMI_PHY_I2C_VLEVCTRL);
379 	}
380 
381 	imx_hdmi_phy_enable_power(sc, 1);
382 
383 	/* toggle TMDS enable */
384 	imx_hdmi_phy_enable_tmds(sc, 0);
385 	imx_hdmi_phy_enable_tmds(sc, 1);
386 
387 	/* gen2 tx power on */
388 	imx_hdmi_phy_gen2_txpwron(sc, 1);
389 	imx_hdmi_phy_gen2_pddq(sc, 0);
390 
391 	/*Wait for PHY PLL lock */
392 	msec = 4;
393 	val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
394 	while (val == 0) {
395 		DELAY(1000);
396 		if (msec-- == 0) {
397 			device_printf(sc->sc_dev, "PHY PLL not locked\n");
398 			return (-1);
399 		}
400 		val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
401 	}
402 
403 	return true;
404 }
405 
406 static void
407 imx_hdmi_phy_init(struct imx_hdmi_softc *sc)
408 {
409 	int i;
410 
411 	/* HDMI Phy spec says to do the phy initialization sequence twice */
412 	for (i = 0 ; i < 2 ; i++) {
413 		imx_hdmi_phy_sel_data_en_pol(sc, 1);
414 		imx_hdmi_phy_sel_interface_control(sc, 0);
415 		imx_hdmi_phy_enable_tmds(sc, 0);
416 		imx_hdmi_phy_enable_power(sc, 0);
417 
418 		/* Enable CSC */
419 		imx_hdmi_phy_configure(sc);
420 	}
421 }
422 
423 static void
424 imx_hdmi_enable_video_path(struct imx_hdmi_softc *sc)
425 {
426 	uint8_t clkdis;
427 
428 	/*
429 	 * Control period timing
430 	 * Values are minimal according to HDMI spec 1.4a
431 	 */
432 	WR1(sc, HDMI_FC_CTRLDUR, 12);
433 	WR1(sc, HDMI_FC_EXCTRLDUR, 32);
434 	WR1(sc, HDMI_FC_EXCTRLSPAC, 1);
435 
436 	/*
437 	 * Bits to fill data lines not used to transmit preamble
438 	 * for channels 0, 1, and 2 respectively
439 	 */
440 	WR1(sc, HDMI_FC_CH0PREAM, 0x0B);
441 	WR1(sc, HDMI_FC_CH1PREAM, 0x16);
442 	WR1(sc, HDMI_FC_CH2PREAM, 0x21);
443 
444 	/* Save CEC clock */
445 	clkdis = RD1(sc, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE;
446 	clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
447 
448 	/* Enable pixel clock and tmds data path */
449 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
450 	WR1(sc, HDMI_MC_CLKDIS, clkdis);
451 
452 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
453 	WR1(sc, HDMI_MC_CLKDIS, clkdis);
454 }
455 
456 static void
457 imx_hdmi_video_packetize(struct imx_hdmi_softc *sc)
458 {
459 	unsigned int color_depth = 0;
460 	unsigned int remap_size = HDMI_VP_REMAP_YCC422_16BIT;
461 	unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
462 	uint8_t val;
463 
464 	output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
465 	color_depth = 0;
466 
467 	/* set the packetizer registers */
468 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
469 		HDMI_VP_PR_CD_COLOR_DEPTH_MASK);
470 	WR1(sc, HDMI_VP_PR_CD, val);
471 
472 	val = RD1(sc, HDMI_VP_STUFF);
473 	val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK;
474 	val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE;
475 	WR1(sc, HDMI_VP_STUFF, val);
476 
477 	val = RD1(sc, HDMI_VP_CONF);
478 	val &= ~(HDMI_VP_CONF_PR_EN_MASK |
479 		HDMI_VP_CONF_BYPASS_SELECT_MASK);
480 	val |= HDMI_VP_CONF_PR_EN_DISABLE |
481 		HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
482 	WR1(sc, HDMI_VP_CONF, val);
483 
484 	val = RD1(sc, HDMI_VP_STUFF);
485 	val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK;
486 	val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET;
487 	WR1(sc, HDMI_VP_STUFF, val);
488 
489 	WR1(sc, HDMI_VP_REMAP, remap_size);
490 
491 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
492 		val = RD1(sc, HDMI_VP_CONF);
493 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
494 			HDMI_VP_CONF_PP_EN_ENMASK |
495 			HDMI_VP_CONF_YCC422_EN_MASK);
496 		val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
497 			HDMI_VP_CONF_PP_EN_ENABLE |
498 			HDMI_VP_CONF_YCC422_EN_DISABLE;
499 		WR1(sc, HDMI_VP_CONF, val);
500 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
501 		val = RD1(sc, HDMI_VP_CONF);
502 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
503 			HDMI_VP_CONF_PP_EN_ENMASK |
504 			HDMI_VP_CONF_YCC422_EN_MASK);
505 		val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
506 			HDMI_VP_CONF_PP_EN_DISABLE |
507 			HDMI_VP_CONF_YCC422_EN_ENABLE;
508 		WR1(sc, HDMI_VP_CONF, val);
509 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
510 		val = RD1(sc, HDMI_VP_CONF);
511 		val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
512 			HDMI_VP_CONF_PP_EN_ENMASK |
513 			HDMI_VP_CONF_YCC422_EN_MASK);
514 		val |= HDMI_VP_CONF_BYPASS_EN_ENABLE |
515 			HDMI_VP_CONF_PP_EN_DISABLE |
516 			HDMI_VP_CONF_YCC422_EN_DISABLE;
517 		WR1(sc, HDMI_VP_CONF, val);
518 	} else {
519 		return;
520 	}
521 
522 	val = RD1(sc, HDMI_VP_STUFF);
523 	val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK |
524 		HDMI_VP_STUFF_YCC422_STUFFING_MASK);
525 	val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
526 		HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE;
527 	WR1(sc, HDMI_VP_STUFF, val);
528 
529 	val = RD1(sc, HDMI_VP_CONF);
530 	val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK;
531 	val |= output_select;
532 	WR1(sc, HDMI_VP_CONF, val);
533 }
534 
535 static void
536 imx_hdmi_video_sample(struct imx_hdmi_softc *sc)
537 {
538 	int color_format;
539 	uint8_t val;
540 
541 	color_format = 0x01;
542 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
543 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
544 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
545 	WR1(sc, HDMI_TX_INVID0, val);
546 
547 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
548 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
549 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
550 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
551 	WR1(sc, HDMI_TX_INSTUFFING, val);
552 	WR1(sc, HDMI_TX_GYDATA0, 0x0);
553 	WR1(sc, HDMI_TX_GYDATA1, 0x0);
554 	WR1(sc, HDMI_TX_RCRDATA0, 0x0);
555 	WR1(sc, HDMI_TX_RCRDATA1, 0x0);
556 	WR1(sc, HDMI_TX_BCBDATA0, 0x0);
557 	WR1(sc, HDMI_TX_BCBDATA1, 0x0);
558 }
559 
560 static int
561 imx_hdmi_set_mode(struct imx_hdmi_softc *sc)
562 {
563 
564 	imx_hdmi_disable_overflow_interrupts(sc);
565 	imx_hdmi_av_composer(sc);
566 	imx_hdmi_phy_init(sc);
567 	imx_hdmi_enable_video_path(sc);
568 	/* TODO: AVI infoframes */
569 	imx_hdmi_video_packetize(sc);
570 	/* TODO:  imx_hdmi_video_csc(sc); */
571 	imx_hdmi_video_sample(sc);
572 	imx_hdmi_clear_overflow(sc);
573 
574 	return (0);
575 }
576 
577 static int
578 hdmi_edid_read(struct imx_hdmi_softc *sc, uint8_t **edid, uint32_t *edid_len)
579 {
580 	device_t i2c_dev;
581 	int result;
582 	uint8_t addr = 0;
583 	struct iic_msg msg[] = {
584 		{ 0, IIC_M_WR, 1, &addr },
585 		{ 0, IIC_M_RD, EDID_LENGTH, NULL}
586 	};
587 
588 	*edid = NULL;
589 	*edid_len = 0;
590 
591 	if (sc->sc_i2c_xref == 0)
592 		return (ENXIO);
593 
594 	i2c_dev = OF_device_from_xref(sc->sc_i2c_xref);
595 	if (!i2c_dev) {
596 		device_printf(sc->sc_dev,
597 		    "no actual device for \"ddc-i2c-bus\" property (handle=%x)\n", sc->sc_i2c_xref);
598 		return (ENXIO);
599 	}
600 
601 	device_printf(sc->sc_dev, "reading EDID from %s, addr %02x\n",
602 	    device_get_nameunit(i2c_dev), I2C_DDC_ADDR/2);
603 
604 	msg[0].slave = I2C_DDC_ADDR;
605 	msg[1].slave = I2C_DDC_ADDR;
606 	msg[1].buf = sc->sc_edid;
607 
608 	result = iicbus_request_bus(i2c_dev, sc->sc_dev, IIC_INTRWAIT);
609 
610 	if (result) {
611 		device_printf(sc->sc_dev, "failed to request i2c bus: %d\n", result);
612 		return (result);
613 	}
614 
615 	result = iicbus_transfer(i2c_dev, msg, 2);
616 	iicbus_release_bus(i2c_dev, sc->sc_dev);
617 
618 	if (result) {
619 		device_printf(sc->sc_dev, "i2c transfer failed: %d\n", result);
620 		return (result);
621 	} else {
622 		*edid_len = sc->sc_edid_len;
623 		*edid = sc->sc_edid;
624 	}
625 
626 	return (result);
627 }
628 
629 static void
630 imx_hdmi_detect_cable(void *arg)
631 {
632 	struct imx_hdmi_softc *sc;
633 
634 	sc = arg;
635 	EVENTHANDLER_INVOKE(hdmi_event, sc->sc_dev, HDMI_EVENT_CONNECTED);
636 	/* Finished with the interrupt hook */
637 	config_intrhook_disestablish(&sc->sc_mode_hook);
638 }
639 
640 static int
641 imx_hdmi_detach(device_t dev)
642 {
643 	struct imx_hdmi_softc *sc;
644 
645 	sc = device_get_softc(dev);
646 
647 	if (sc->sc_mem_res != NULL)
648 		bus_release_resource(dev, SYS_RES_MEMORY,
649 		    sc->sc_mem_rid, sc->sc_mem_res);
650 
651 	return (0);
652 }
653 
654 static int
655 imx_hdmi_attach(device_t dev)
656 {
657 	struct imx_hdmi_softc *sc;
658 	int err;
659 	uint32_t gpr3;
660 	phandle_t node, i2c_xref;
661 
662 	sc = device_get_softc(dev);
663 	sc->sc_dev = dev;
664 	err = 0;
665 
666 	/* Allocate memory resources. */
667 	sc->sc_mem_rid = 0;
668 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mem_rid,
669 	    RF_ACTIVE);
670 	if (sc->sc_mem_res == NULL) {
671 		device_printf(dev, "Cannot allocate memory resources\n");
672 		err = ENXIO;
673 		goto out;
674 	}
675 
676 	sc->sc_mode_hook.ich_func = imx_hdmi_detect_cable;
677 	sc->sc_mode_hook.ich_arg = sc;
678 
679 	if (config_intrhook_establish(&sc->sc_mode_hook) != 0) {
680 		err = ENOMEM;
681 		goto out;
682 	}
683 
684 	node = ofw_bus_get_node(dev);
685 	if (OF_getencprop(node, "ddc-i2c-bus", &i2c_xref, sizeof(i2c_xref)) == -1)
686 		sc->sc_i2c_xref = 0;
687 	else
688 		sc->sc_i2c_xref = i2c_xref;
689 
690 	sc->sc_edid = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO);
691 	sc->sc_edid_len = EDID_LENGTH;
692 
693 	imx_ccm_hdmi_enable();
694 
695 	device_printf(sc->sc_dev, "HDMI controller %02x:%02x:%02x:%02x\n",
696 	    RD1(sc, HDMI_DESIGN_ID), RD1(sc, HDMI_REVISION_ID),
697 	    RD1(sc, HDMI_PRODUCT_ID0), RD1(sc, HDMI_PRODUCT_ID1));
698 
699 
700 	gpr3 = imx_iomux_gpr_get(IOMUXC_GPR3);
701 	gpr3 &= ~(IOMUXC_GPR3_HDMI_MASK);
702 	gpr3 |= IOMUXC_GPR3_HDMI_IPU1_DI0;
703 	imx_iomux_gpr_set(IOMUXC_GPR3, gpr3);
704 
705 	WR1(sc, HDMI_PHY_POL0, HDMI_PHY_HPD);
706 	WR1(sc, HDMI_IH_PHY_STAT0, HDMI_IH_PHY_STAT0_HPD);
707 
708 out:
709 
710 	if (err != 0)
711 		imx_hdmi_detach(dev);
712 
713 	return (err);
714 }
715 
716 static int
717 imx_hdmi_probe(device_t dev)
718 {
719 
720 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
721 		return (ENXIO);
722 
723 	device_set_desc(dev, "Freescale i.MX6 HDMI core");
724 
725 	return (BUS_PROBE_DEFAULT);
726 }
727 
728 static int
729 imx_hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
730 {
731 
732 	return (hdmi_edid_read(device_get_softc(dev), edid, edid_len));
733 }
734 
735 static int
736 imx_hdmi_set_videomode(device_t dev, const struct videomode *mode)
737 {
738 	struct imx_hdmi_softc *sc;
739 
740 	sc = device_get_softc(dev);
741 	memcpy(&sc->sc_mode, mode, sizeof(*mode));
742 	imx_hdmi_set_mode(sc);
743 
744 	return (0);
745 }
746 
747 static device_method_t imx_hdmi_methods[] = {
748 	/* Device interface */
749 	DEVMETHOD(device_probe,  imx_hdmi_probe),
750 	DEVMETHOD(device_attach, imx_hdmi_attach),
751 	DEVMETHOD(device_detach, imx_hdmi_detach),
752 
753 	/* HDMI methods */
754 	DEVMETHOD(hdmi_get_edid,	imx_hdmi_get_edid),
755 	DEVMETHOD(hdmi_set_videomode,	imx_hdmi_set_videomode),
756 
757 	DEVMETHOD_END
758 };
759 
760 static driver_t imx_hdmi_driver = {
761 	"hdmi",
762 	imx_hdmi_methods,
763 	sizeof(struct imx_hdmi_softc)
764 };
765 
766 static devclass_t imx_hdmi_devclass;
767 
768 DRIVER_MODULE(hdmi, simplebus, imx_hdmi_driver, imx_hdmi_devclass, 0, 0);
769