xref: /openbsd/sys/arch/armv7/omap/nxphdmi.c (revision 3cab2bb3)
1 /* $OpenBSD: nxphdmi.c,v 1.6 2019/05/06 03:45:58 mlarkin Exp $ */
2 /*
3  * Copyright (c) 2016 Ian Sutton <ians@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
30  * All rights reserved.
31  *
32  * Redistribution and use in source and binary forms, with or without
33  * modification, are permitted provided that the following conditions
34  * are met:
35  * 1. Redistributions of source code must retain the above copyright
36  *    notice, this list of conditions and the following disclaimer.
37  * 2. Redistributions in binary form must reproduce the above copyright
38  *    notice, this list of conditions and the following disclaimer in the
39  *    documentation and/or other materials provided with the distribution.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  */
53 
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/device.h>
57 #include <sys/errno.h>
58 
59 #include <dev/i2c/i2cvar.h>
60 #include <dev/videomode/videomode.h>
61 
62 #include <dev/ofw/openfirm.h>
63 #include <dev/ofw/ofw_pinctrl.h>
64 
65 #include <arch/armv7/omap/nxphdmivar.h>
66 
67 /* TDA19988 registers */
68 #define	MKREG(page, addr)	(((page) << 8) | (addr))
69 
70 #define	REGPAGE(reg)		(((reg) >> 8) & 0xff)
71 #define	REGADDR(reg)		((reg) & 0xff)
72 
73 #define	TDA_VERSION		MKREG(0x00, 0x00)
74 #define	TDA_MAIN_CNTRL0		MKREG(0x00, 0x01)
75 #define		MAIN_CNTRL0_SR		(1 << 0)
76 #define	TDA_VERSION_MSB		MKREG(0x00, 0x02)
77 #define	TDA_SOFTRESET		MKREG(0x00, 0x0a)
78 #define		SOFTRESET_I2C		(1 << 1)
79 #define		SOFTRESET_AUDIO		(1 << 0)
80 #define	TDA_DDC_CTRL		MKREG(0x00, 0x0b)
81 #define		DDC_ENABLE		0
82 #define	TDA_CCLK		MKREG(0x00, 0x0c)
83 #define		CCLK_ENABLE		1
84 #define	TDA_INT_FLAGS_2		MKREG(0x00, 0x11)
85 #define		INT_FLAGS_2_EDID_BLK_RD	(1 << 1)
86 
87 #define	TDA_VIP_CNTRL_0		MKREG(0x00, 0x20)
88 #define	TDA_VIP_CNTRL_1		MKREG(0x00, 0x21)
89 #define	TDA_VIP_CNTRL_2		MKREG(0x00, 0x22)
90 #define	TDA_VIP_CNTRL_3		MKREG(0x00, 0x23)
91 #define		VIP_CNTRL_3_SYNC_HS	(2 << 4)
92 #define		VIP_CNTRL_3_V_TGL	(1 << 2)
93 #define		VIP_CNTRL_3_H_TGL	(1 << 1)
94 
95 #define	TDA_VIP_CNTRL_4		MKREG(0x00, 0x24)
96 #define		VIP_CNTRL_4_BLANKIT_NDE		(0 << 2)
97 #define		VIP_CNTRL_4_BLANKIT_HS_VS	(1 << 2)
98 #define		VIP_CNTRL_4_BLANKIT_NHS_VS	(2 << 2)
99 #define		VIP_CNTRL_4_BLANKIT_HE_VE	(3 << 2)
100 #define		VIP_CNTRL_4_BLC_NONE		(0 << 0)
101 #define		VIP_CNTRL_4_BLC_RGB444		(1 << 0)
102 #define		VIP_CNTRL_4_BLC_YUV444		(2 << 0)
103 #define		VIP_CNTRL_4_BLC_YUV422		(3 << 0)
104 #define	TDA_VIP_CNTRL_5		MKREG(0x00, 0x25)
105 #define		VIP_CNTRL_5_SP_CNT(n)	(((n) & 3) << 1)
106 #define	TDA_MUX_VP_VIP_OUT	MKREG(0x00, 0x27)
107 #define	TDA_MAT_CONTRL		MKREG(0x00, 0x80)
108 #define		MAT_CONTRL_MAT_BP	(1 << 2)
109 #define	TDA_VIDFORMAT		MKREG(0x00, 0xa0)
110 #define	TDA_REFPIX_MSB		MKREG(0x00, 0xa1)
111 #define	TDA_REFPIX_LSB		MKREG(0x00, 0xa2)
112 #define	TDA_REFLINE_MSB		MKREG(0x00, 0xa3)
113 #define	TDA_REFLINE_LSB		MKREG(0x00, 0xa4)
114 #define	TDA_NPIX_MSB		MKREG(0x00, 0xa5)
115 #define	TDA_NPIX_LSB		MKREG(0x00, 0xa6)
116 #define	TDA_NLINE_MSB		MKREG(0x00, 0xa7)
117 #define	TDA_NLINE_LSB		MKREG(0x00, 0xa8)
118 #define	TDA_VS_LINE_STRT_1_MSB	MKREG(0x00, 0xa9)
119 #define	TDA_VS_LINE_STRT_1_LSB	MKREG(0x00, 0xaa)
120 #define	TDA_VS_PIX_STRT_1_MSB	MKREG(0x00, 0xab)
121 #define	TDA_VS_PIX_STRT_1_LSB	MKREG(0x00, 0xac)
122 #define	TDA_VS_LINE_END_1_MSB	MKREG(0x00, 0xad)
123 #define	TDA_VS_LINE_END_1_LSB	MKREG(0x00, 0xae)
124 #define	TDA_VS_PIX_END_1_MSB	MKREG(0x00, 0xaf)
125 #define	TDA_VS_PIX_END_1_LSB	MKREG(0x00, 0xb0)
126 #define	TDA_VS_LINE_STRT_2_MSB	MKREG(0x00, 0xb1)
127 #define	TDA_VS_LINE_STRT_2_LSB	MKREG(0x00, 0xb2)
128 #define	TDA_VS_PIX_STRT_2_MSB	MKREG(0x00, 0xb3)
129 #define	TDA_VS_PIX_STRT_2_LSB	MKREG(0x00, 0xb4)
130 #define	TDA_VS_LINE_END_2_MSB	MKREG(0x00, 0xb5)
131 #define	TDA_VS_LINE_END_2_LSB	MKREG(0x00, 0xb6)
132 #define	TDA_VS_PIX_END_2_MSB	MKREG(0x00, 0xb7)
133 #define	TDA_VS_PIX_END_2_LSB	MKREG(0x00, 0xb8)
134 #define	TDA_HS_PIX_START_MSB	MKREG(0x00, 0xb9)
135 #define	TDA_HS_PIX_START_LSB	MKREG(0x00, 0xba)
136 #define	TDA_HS_PIX_STOP_MSB	MKREG(0x00, 0xbb)
137 #define	TDA_HS_PIX_STOP_LSB	MKREG(0x00, 0xbc)
138 #define	TDA_VWIN_START_1_MSB	MKREG(0x00, 0xbd)
139 #define	TDA_VWIN_START_1_LSB	MKREG(0x00, 0xbe)
140 #define	TDA_VWIN_END_1_MSB	MKREG(0x00, 0xbf)
141 #define	TDA_VWIN_END_1_LSB	MKREG(0x00, 0xc0)
142 #define	TDA_VWIN_START_2_MSB	MKREG(0x00, 0xc1)
143 #define	TDA_VWIN_START_2_LSB	MKREG(0x00, 0xc2)
144 #define	TDA_VWIN_END_2_MSB	MKREG(0x00, 0xc3)
145 #define	TDA_VWIN_END_2_LSB	MKREG(0x00, 0xc4)
146 #define	TDA_DE_START_MSB	MKREG(0x00, 0xc5)
147 #define	TDA_DE_START_LSB	MKREG(0x00, 0xc6)
148 #define	TDA_DE_STOP_MSB		MKREG(0x00, 0xc7)
149 #define	TDA_DE_STOP_LSB		MKREG(0x00, 0xc8)
150 
151 #define	TDA_TBG_CNTRL_0		MKREG(0x00, 0xca)
152 #define		TBG_CNTRL_0_SYNC_ONCE	(1 << 7)
153 #define		TBG_CNTRL_0_SYNC_MTHD	(1 << 6)
154 
155 #define	TDA_TBG_CNTRL_1		MKREG(0x00, 0xcb)
156 #define		TBG_CNTRL_1_DWIN_DIS	(1 << 6)
157 #define		TBG_CNTRL_1_TGL_EN	(1 << 2)
158 #define		TBG_CNTRL_1_V_TGL	(1 << 1)
159 #define		TBG_CNTRL_1_H_TGL	(1 << 0)
160 
161 #define	TDA_HVF_CNTRL_0		MKREG(0x00, 0xe4)
162 #define		HVF_CNTRL_0_PREFIL_NONE		(0 << 2)
163 #define		HVF_CNTRL_0_INTPOL_BYPASS	(0 << 0)
164 #define	TDA_HVF_CNTRL_1		MKREG(0x00, 0xe5)
165 #define		HVF_CNTRL_1_VQR(x)	(((x) & 3) << 2)
166 #define		HVF_CNTRL_1_VQR_FULL	HVF_CNTRL_1_VQR(0)
167 #define	TDA_ENABLE_SPACE	MKREG(0x00, 0xd6)
168 #define	TDA_RPT_CNTRL		MKREG(0x00, 0xf0)
169 
170 #define	TDA_PLL_SERIAL_1	MKREG(0x02, 0x00)
171 #define		PLL_SERIAL_1_SRL_MAN_IP	(1 << 6)
172 #define	TDA_PLL_SERIAL_2	MKREG(0x02, 0x01)
173 #define		PLL_SERIAL_2_SRL_PR(x)		(((x) & 0xf) << 4)
174 #define		PLL_SERIAL_2_SRL_NOSC(x)	(((x) & 0x3) << 0)
175 #define	TDA_PLL_SERIAL_3	MKREG(0x02, 0x02)
176 #define		PLL_SERIAL_3_SRL_PXIN_SEL	(1 << 4)
177 #define		PLL_SERIAL_3_SRL_DE		(1 << 2)
178 #define		PLL_SERIAL_3_SRL_CCIR		(1 << 0)
179 #define	TDA_SERIALIZER		MKREG(0x02, 0x03)
180 #define	TDA_BUFFER_OUT		MKREG(0x02, 0x04)
181 #define	TDA_PLL_SCG1		MKREG(0x02, 0x05)
182 #define	TDA_PLL_SCG2		MKREG(0x02, 0x06)
183 #define	TDA_PLL_SCGN1		MKREG(0x02, 0x07)
184 #define	TDA_PLL_SCGN2		MKREG(0x02, 0x08)
185 #define	TDA_PLL_SCGR1		MKREG(0x02, 0x09)
186 #define	TDA_PLL_SCGR2		MKREG(0x02, 0x0a)
187 
188 #define	TDA_SEL_CLK		MKREG(0x02, 0x11)
189 #define		SEL_CLK_ENA_SC_CLK	(1 << 3)
190 #define		SEL_CLK_SEL_VRF_CLK(x)	(((x) & 3) << 1)
191 #define		SEL_CLK_SEL_CLK1	(1 << 0)
192 #define	TDA_ANA_GENERAL		MKREG(0x02, 0x12)
193 
194 #define	TDA_EDID_DATA0		MKREG(0x09, 0x00)
195 #define	TDA_EDID_CTRL		MKREG(0x09, 0xfa)
196 #define	TDA_DDC_ADDR		MKREG(0x09, 0xfb)
197 #define	TDA_DDC_OFFS		MKREG(0x09, 0xfc)
198 #define	TDA_DDC_SEGM_ADDR	MKREG(0x09, 0xfd)
199 #define	TDA_DDC_SEGM		MKREG(0x09, 0xfe)
200 
201 #define	TDA_IF_VSP		MKREG(0x10, 0x20)
202 #define	TDA_IF_AVI		MKREG(0x10, 0x40)
203 #define	TDA_IF_SPD		MKREG(0x10, 0x60)
204 #define	TDA_IF_AUD		MKREG(0x10, 0x80)
205 #define	TDA_IF_MPS		MKREG(0x10, 0xa0)
206 
207 #define	TDA_ENC_CNTRL		MKREG(0x11, 0x0d)
208 #define		ENC_CNTRL_DVI_MODE	(0 << 2)
209 #define		ENC_CNTRL_HDMI_MODE	(1 << 2)
210 #define	TDA_DIP_IF_FLAGS	MKREG(0x11, 0x0f)
211 #define		DIP_IF_FLAGS_IF5	(1 << 5)
212 #define		DIP_IF_FLAGS_IF4	(1 << 4)
213 #define		DIP_IF_FLAGS_IF3	(1 << 3)
214 #define		DIP_IF_FLAGS_IF2	(1 << 2) /*  AVI IF on page 10h */
215 #define		DIP_IF_FLAGS_IF1	(1 << 1)
216 
217 #define	TDA_TX3			MKREG(0x12, 0x9a)
218 #define	TDA_TX4			MKREG(0x12, 0x9b)
219 #define		TX4_PD_RAM		(1 << 1)
220 #define	TDA_HDCP_TX33		MKREG(0x12, 0xb8)
221 #define		HDCP_TX33_HDMI		(1 << 1)
222 
223 #define	TDA_CURPAGE_ADDR	0xff
224 
225 #define	TDA_CEC_ENAMODS		0xff
226 #define		ENAMODS_RXSENS		(1 << 2)
227 #define		ENAMODS_HDMI		(1 << 1)
228 #define	TDA_CEC_FRO_IM_CLK_CTRL	0xfb
229 #define		CEC_FRO_IM_CLK_CTRL_GHOST_DIS	(1 << 7)
230 #define		CEC_FRO_IM_CLK_CTRL_IMCLK_SEL	(1 << 1)
231 
232 /* EDID reading */
233 #define	EDID_LENGTH		0x80
234 #define	MAX_READ_ATTEMPTS	100
235 
236 /* EDID fields */
237 #define	EDID_MODES0		35
238 #define	EDID_MODES1		36
239 #define	EDID_TIMING_START	38
240 #define	EDID_TIMING_END		54
241 #define	EDID_TIMING_X(v)	(((v) + 31) * 8)
242 #define	EDID_FREQ(v)		(((v) & 0x3f) + 60)
243 #define	EDID_RATIO(v)		(((v) >> 6) & 0x3)
244 #define	EDID_RATIO_10x16	0
245 #define	EDID_RATIO_3x4		1
246 #define	EDID_RATIO_4x5		2
247 #define	EDID_RATIO_9x16		3
248 
249 /* NXP TDA19988 slave addrs. */
250 #define	TDA_HDMI		0x70
251 #define	TDA_CEC			0x34
252 
253 /* debug/etc macros */
254 #define DEVNAME(s)		((s)->sc_dev.dv_xname)
255 #ifdef NXPTDA_DEBUG
256 int nxphdmi_debug = 1;
257 #define DPRINTF(n,s)	do { if ((n) <= nxphdmi_debug) printf s; } while (0)
258 #else
259 #define DPRINTF(n,s)	do {} while (0)
260 #endif
261 
262 struct nxphdmi_softc {
263 	struct device	sc_dev;
264 	i2c_tag_t	sc_tag;
265 	i2c_addr_t	sc_addr;
266 
267 	uint8_t		sc_curpage;
268 	uint8_t		sc_edid[EDID_LENGTH];
269 };
270 
271 int	nxphdmi_match(struct device *, void *, void *);
272 void	nxphdmi_attach(struct device *, struct device *, void *);
273 
274 int	nxphdmi_cec_read(struct nxphdmi_softc *, uint8_t, uint8_t *);
275 int	nxphdmi_cec_write(struct nxphdmi_softc *, uint8_t, uint8_t);
276 int	nxphdmi_read(struct nxphdmi_softc *, uint16_t, uint8_t *);
277 int	nxphdmi_write(struct nxphdmi_softc *, uint16_t, uint8_t);
278 int	nxphdmi_write2(struct nxphdmi_softc *, uint16_t, uint16_t);
279 int	nxphdmi_set(struct nxphdmi_softc *, uint16_t, uint8_t);
280 int	nxphdmi_clear(struct nxphdmi_softc *, uint16_t, uint8_t);
281 int	nxphdmi_set_page(struct nxphdmi_softc *, uint8_t);
282 int	nxphdmi_read_edid(struct nxphdmi_softc *);
283 int	nxphdmi_reset(struct nxphdmi_softc *);
284 int	nxphdmi_init_encoder(struct nxphdmi_softc *, struct videomode *);
285 
286 int	nxphdmi_get_edid(uint8_t *, int);
287 int	nxphdmi_set_videomode(struct videomode *);
288 
289 struct cfattach nxphdmi_ca = {
290 	sizeof(struct nxphdmi_softc), nxphdmi_match, nxphdmi_attach
291 };
292 
293 struct cfdriver nxphdmi_cd = {
294 	NULL, "nxphdmi", DV_DULL
295 };
296 
297 int
298 nxphdmi_match(struct device *parent, void *match, void *aux)
299 {
300 	struct i2c_attach_args *ia = aux;
301 
302 	if (strcmp(ia->ia_name, "nxp,tda998x") == 0)
303 		return 1;
304 
305 	return 0;
306 }
307 
308 void
309 nxphdmi_attach(struct device *parent, struct device *self, void *aux)
310 {
311 	struct nxphdmi_softc *sc = (struct nxphdmi_softc *)self;
312 	struct i2c_attach_args *ia = aux;
313 	uint8_t data = 0;
314 	uint16_t version = 0;
315 	int res = 0, node = *(int *)(ia->ia_cookie);
316 
317 	sc->sc_tag = ia->ia_tag;
318 	sc->sc_addr = ia->ia_addr;
319 	sc->sc_curpage = 0xff;
320 
321 	if (!node) {
322 		printf(": not configured\n");
323 		return;
324 	} else if ((pinctrl_byname(node, "default") == -1)) {
325 		printf(": not configured\n");
326 		return;
327 	}
328 
329 	iic_acquire_bus(sc->sc_tag, 0);
330 
331 	DPRINTF(3,("\n"));
332 
333 	/* enable HDMI core */
334 	nxphdmi_cec_write(sc, TDA_CEC_ENAMODS, ENAMODS_RXSENS | ENAMODS_HDMI);
335 	delay(1000);
336 
337 	if (!(nxphdmi_reset(sc)))
338 		DPRINTF(3,("%s: software reset OK\n", DEVNAME(sc)));
339 	else
340 		DPRINTF(3,("%s: software reset failed!\n", DEVNAME(sc)));
341 
342 	/*  PLL registers common configuration */
343 	nxphdmi_write(sc, TDA_PLL_SERIAL_1, 0x00);
344 	nxphdmi_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
345 	nxphdmi_write(sc, TDA_PLL_SERIAL_3, 0x00);
346 	nxphdmi_write(sc, TDA_SERIALIZER, 0x00);
347 	nxphdmi_write(sc, TDA_BUFFER_OUT, 0x00);
348 	nxphdmi_write(sc, TDA_PLL_SCG1, 0x00);
349 	nxphdmi_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
350 	nxphdmi_write(sc, TDA_PLL_SCGN1, 0xfa);
351 	nxphdmi_write(sc, TDA_PLL_SCGN2, 0x00);
352 	nxphdmi_write(sc, TDA_PLL_SCGR1, 0x5b);
353 	nxphdmi_write(sc, TDA_PLL_SCGR2, 0x00);
354 	nxphdmi_write(sc, TDA_PLL_SCG2, 0x10);
355 
356 	/*  Write the default value MUX register */
357 	nxphdmi_write(sc, TDA_MUX_VP_VIP_OUT, 0x24);
358 
359 	res |= nxphdmi_read(sc, TDA_VERSION, &data);
360 	version |= data;
361 	res |= nxphdmi_read(sc, TDA_VERSION_MSB, &data);
362 	version |= (data << 8);
363 	version &= ~0x30;
364 
365 	if (!res) {
366 		DPRINTF(3,("%s: ", DEVNAME(sc)));
367 		printf(": rev 0x%04x\n", version);
368 	} else {
369 		DPRINTF(3,("%s: ", DEVNAME(sc)));
370 		printf(": failed to enable HDMI core, exiting...\n");
371 		iic_release_bus(sc->sc_tag, 0);
372 		return;
373 	}
374 
375 	nxphdmi_write(sc, TDA_DDC_CTRL, DDC_ENABLE);
376 	nxphdmi_write(sc, TDA_TX3, 39);
377 
378 	nxphdmi_cec_write(sc, TDA_CEC_FRO_IM_CLK_CTRL,
379 	    CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
380 
381 	if (nxphdmi_read_edid(sc)) {
382 		DPRINTF(3,("%s: failed to read EDID bits, exiting!\n",
383 		    DEVNAME(sc)));
384 		return;
385 	}
386 
387 	/*  Default values for RGB 4:4:4 mapping */
388 	nxphdmi_write(sc, TDA_VIP_CNTRL_0, 0x23);
389 	nxphdmi_write(sc, TDA_VIP_CNTRL_1, 0x01);
390 	nxphdmi_write(sc, TDA_VIP_CNTRL_2, 0x45);
391 
392 	iic_release_bus(sc->sc_tag, 0);
393 }
394 
395 int
396 nxphdmi_cec_read(struct nxphdmi_softc *sc, uint8_t addr, uint8_t *buf)
397 {
398 	int ret = 0;
399 
400 	if ((ret |= iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, TDA_CEC,
401 	    &addr, 1, NULL, 0, 0))) {
402 		DPRINTF(3,("%s: (CEC) failed to read addr 0x%02x, errno %d\n",
403 		    DEVNAME(sc), addr, ret));
404 		return ret;
405 	}
406 
407 	DPRINTF(3,("%s: (CEC) read 0x%02x from 0x%02x\n", DEVNAME(sc), *buf,
408 	    addr));
409 
410 	return ret;
411 }
412 
413 int
414 nxphdmi_cec_write(struct nxphdmi_softc *sc, uint8_t addr, uint8_t val)
415 {
416 	int ret = 0;
417 	uint8_t sendbuf[] = { addr, val };
418 
419 	if ((ret |= iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_CEC,
420 	    &sendbuf, 2, NULL, 0, 0))) {
421 		DPRINTF(3,(
422 		    "%s: (CEC) failed to write 0x%02x to 0x%02x, errno %d\n",
423 		    DEVNAME(sc), val, addr, ret));
424 		return ret;
425 	}
426 
427 	DPRINTF(3,("%s: (CEC) wrote 0x%02x to 0x%02x\n", DEVNAME(sc), val,
428 	    addr));
429 
430 	return ret;
431 }
432 
433 int
434 nxphdmi_read(struct nxphdmi_softc *sc, uint16_t reg, uint8_t *buf)
435 {
436 	int ret = 0;
437 
438 	nxphdmi_set_page(sc, REGPAGE(reg));
439 
440 	if ((ret = iic_smbus_read_byte(sc->sc_tag, TDA_HDMI, REGADDR(reg),
441 	    buf, 0))) {
442 		DPRINTF(3,(
443 		    "%s: failed to read addr 0x%02x on page 0x%02x, errno %d\n",
444 		    DEVNAME(sc), REGADDR(reg), REGPAGE(reg), ret));
445 		return ret;
446 	}
447 
448 	DPRINTF(3,("%s: read  0x%02x from 0x%02x on page 0x%02x\n",
449 	    DEVNAME(sc), *buf, REGADDR(reg), REGPAGE(reg)));
450 
451 	return ret;
452 }
453 
454 int
455 nxphdmi_write(struct nxphdmi_softc *sc, uint16_t reg, uint8_t val)
456 {
457 	int ret = 0;
458 	uint8_t sendbuf[] = { REGADDR(reg), val };
459 
460 	nxphdmi_set_page(sc, REGPAGE(reg));
461 
462 	if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI,
463 	    &sendbuf, 2, NULL, 0, 0))) {
464 	    DPRINTF(3,(
465 		"%s: failed to write 0x%02x to 0x%02x on page 0x%02x, errno %d\n",
466 		DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg), ret));
467 		return ret;
468 	}
469 
470 	DPRINTF(3,("%s: wrote 0x%02x  to  0x%02x on page 0x%02x\n",
471 	    DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg)));
472 
473 	return ret;
474 }
475 
476 int
477 nxphdmi_write2(struct nxphdmi_softc *sc, uint16_t reg, uint16_t val)
478 {
479 	int ret = 0;
480 	uint8_t sendbuf[] = { REGADDR(reg), val >> 8, val & 0xff };
481 
482 	nxphdmi_set_page(sc, REGPAGE(reg));
483 
484 	if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI,
485 	    &sendbuf, 3, NULL, 0, 0))) {
486 		DPRINTF(3,(
487 		"%s: failed to write 0x%04x to 0x%02x on page 0x%02x, errno %d\n",
488 		DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg), ret));
489 		return ret;
490 	}
491 
492 	DPRINTF(3,("%s: wrote 0x%04x  to  0x%02x on page 0x%02x\n",
493 	    DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg)));
494 
495 	return ret;
496 
497 }
498 
499 int
500 nxphdmi_set(struct nxphdmi_softc *sc, uint16_t reg, uint8_t bits)
501 {
502 	int ret = 0;
503 	uint8_t buf;
504 
505 	ret |= nxphdmi_read(sc, reg, &buf);
506 	buf |= bits;
507 	ret |= nxphdmi_write(sc, reg, buf);
508 
509 	return ret;
510 }
511 
512 int
513 nxphdmi_clear(struct nxphdmi_softc *sc, uint16_t reg, uint8_t bits)
514 {
515 	int ret = 0;
516 	uint8_t buf;
517 
518 	ret |= nxphdmi_read(sc, reg, &buf);
519 	buf &= ~bits;
520 	ret |= nxphdmi_write(sc, reg, buf);
521 
522 	return ret;
523 }
524 
525 int
526 nxphdmi_set_page(struct nxphdmi_softc *sc, uint8_t page)
527 {
528 	int ret = 0;
529 	uint8_t sendbuf[] = { TDA_CURPAGE_ADDR, page };
530 
531 	if (sc->sc_curpage == page)
532 		return ret;
533 
534 	if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI,
535 	    &sendbuf, sizeof(sendbuf), NULL, 0, 0))) {
536 		DPRINTF(3,("%s: failed to set memory page 0x%02x, errno %d\n",
537 		    DEVNAME(sc),
538 		page, ret));
539 		return ret;
540 	}
541 
542 	sc->sc_curpage = page;
543 	DPRINTF(3,("%s: set page to 0x%02x\n", DEVNAME(sc), page));
544 
545 	return ret;
546 }
547 
548 int
549 nxphdmi_read_edid(struct nxphdmi_softc *sc)
550 {
551 	int i = 0, ret = 0;
552 	uint8_t reg;
553 
554 	nxphdmi_set(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
555 
556 	/*  Block 0 */
557 	nxphdmi_write(sc, TDA_DDC_ADDR, 0xa0);
558 	nxphdmi_write(sc, TDA_DDC_OFFS, 0x00);
559 	nxphdmi_write(sc, TDA_DDC_SEGM_ADDR, 0x60);
560 	nxphdmi_write(sc, TDA_DDC_SEGM, 0x00);
561 
562 	nxphdmi_write(sc, TDA_EDID_CTRL, 1);
563 	nxphdmi_write(sc, TDA_EDID_CTRL, 0);
564 
565 	for (; i < MAX_READ_ATTEMPTS; i++) {
566 		nxphdmi_read(sc, TDA_INT_FLAGS_2, &reg);
567 		if (reg & INT_FLAGS_2_EDID_BLK_RD) {
568 			DPRINTF(3,("%s: EDID-ready IRQ fired\n", DEVNAME(sc)));
569 			break;
570 		}
571 	}
572 
573 	if (i == MAX_READ_ATTEMPTS) {
574 		printf("%s: no display detected\n", DEVNAME(sc));
575 		ret = ENXIO;
576 		return ret;
577 	}
578 
579 	nxphdmi_set_page(sc, 0x09);
580 
581 	reg = 0x00;
582 	DPRINTF(1,("%s: ------------- EDID -------------", DEVNAME(sc)));
583 	for (i = 0; i < EDID_LENGTH; i++) {
584 		iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, TDA_HDMI, &reg, 1,
585 		    &sc->sc_edid[i], 1, 0);
586 		if (!(i % 16))
587 			DPRINTF(1,("\n%s: ", DEVNAME(sc)));
588 		DPRINTF(1,("%02x", sc->sc_edid[i]));
589 		reg++;
590 	}
591 	DPRINTF(1,("\n%s: --------------------------------\n", DEVNAME(sc)));
592 
593 	nxphdmi_clear(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
594 	nxphdmi_set(sc, TDA_TX4, TX4_PD_RAM);
595 
596 	return ret;
597 }
598 
599 int
600 nxphdmi_reset(struct nxphdmi_softc *sc)
601 {
602 	int ret = 0;
603 
604 	/* reset core */
605 	ret |= nxphdmi_set(sc, TDA_SOFTRESET, 3);
606 	delay(100);
607 	ret |= nxphdmi_clear(sc, TDA_SOFTRESET, 3);
608 	delay(100);
609 
610 	/* reset transmitter */
611 	ret |= nxphdmi_set(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
612 	ret |= nxphdmi_clear(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
613 
614 	return ret;
615 }
616 
617 int
618 nxphdmi_init_encoder(struct nxphdmi_softc *sc, struct videomode *mode)
619 {
620 	int ret = 0;
621 
622 	uint16_t ref_pix, ref_line, n_pix, n_line;
623 	uint16_t hs_pix_start, hs_pix_stop;
624 	uint16_t vs1_pix_start, vs1_pix_stop;
625 	uint16_t vs1_line_start, vs1_line_end;
626 	uint16_t vs2_pix_start, vs2_pix_stop;
627 	uint16_t vs2_line_start, vs2_line_end;
628 	uint16_t vwin1_line_start, vwin1_line_end;
629 	uint16_t vwin2_line_start, vwin2_line_end;
630 	uint16_t de_start, de_stop;
631 	uint8_t reg, div;
632 
633 	n_pix = mode->htotal;
634 	n_line = mode->vtotal;
635 
636 	hs_pix_stop = mode->hsync_end - mode->hdisplay;
637 	hs_pix_start = mode->hsync_start - mode->hdisplay;
638 
639 	de_stop = mode->htotal;
640 	de_start = mode->htotal - mode->hdisplay;
641 	ref_pix = hs_pix_start + 3;
642 
643 	if (mode->flags & VID_HSKEW)
644 		ref_pix += mode->hsync_end - mode->hsync_start;
645 
646 	if ((mode->flags & VID_INTERLACE) == 0) {
647 		ref_line = 1 + mode->vsync_start - mode->vdisplay;
648 		vwin1_line_start = mode->vtotal - mode->vdisplay - 1;
649 		vwin1_line_end = vwin1_line_start + mode->vdisplay;
650 
651 		vs1_pix_start = vs1_pix_stop = hs_pix_start;
652 		vs1_line_start = mode->vsync_start - mode->vdisplay;
653 		vs1_line_end = vs1_line_start +
654 		    mode->vsync_end - mode->vsync_start;
655 
656 		vwin2_line_start = vwin2_line_end = 0;
657 		vs2_pix_start = vs2_pix_stop = 0;
658 		vs2_line_start = vs2_line_end = 0;
659 	} else {
660 		ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2;
661 		vwin1_line_start = (mode->vtotal - mode->vdisplay)/2;
662 		vwin1_line_end = vwin1_line_start + mode->vdisplay/2;
663 
664 		vs1_pix_start = vs1_pix_stop = hs_pix_start;
665 		vs1_line_start = (mode->vsync_start - mode->vdisplay)/2;
666 		vs1_line_end = vs1_line_start +
667 		    (mode->vsync_end - mode->vsync_start)/2;
668 
669 		vwin2_line_start = vwin1_line_start + mode->vtotal/2;
670 		vwin2_line_end = vwin2_line_start + mode->vdisplay/2;
671 
672 		vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal/2;
673 		vs2_line_start = vs1_line_start + mode->vtotal/2 ;
674 		vs2_line_end = vs2_line_start +
675 		    (mode->vsync_end - mode->vsync_start)/2;
676 	}
677 
678 	div = 148500 / mode->dot_clock;
679 	if (div != 0) {
680 		div--;
681 		if (div > 3)
682 			div = 3;
683 	}
684 
685 	/*  set HDMI HDCP mode off */
686 	nxphdmi_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
687 	nxphdmi_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI);
688 	nxphdmi_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE);
689 
690 	/*  no pre-filter or interpolator */
691 	nxphdmi_write(sc, TDA_HVF_CNTRL_0,
692 	    HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE);
693 	nxphdmi_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
694 	nxphdmi_write(sc, TDA_VIP_CNTRL_4,
695 	    VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE);
696 
697 	nxphdmi_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR);
698 	nxphdmi_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP);
699 	nxphdmi_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE);
700 	nxphdmi_write(sc, TDA_SERIALIZER, 0);
701 	nxphdmi_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL);
702 
703 	nxphdmi_write(sc, TDA_RPT_CNTRL, 0);
704 	nxphdmi_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
705 			SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
706 
707 	nxphdmi_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
708 			PLL_SERIAL_2_SRL_PR(0));
709 
710 	nxphdmi_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP);
711 
712 	nxphdmi_write(sc, TDA_ANA_GENERAL, 0x09);
713 
714 	nxphdmi_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD);
715 
716 	/*
717 	 * Sync on rising HSYNC/VSYNC
718 	 */
719 	reg = VIP_CNTRL_3_SYNC_HS;
720 	if (mode->flags & VID_NHSYNC)
721 		reg |= VIP_CNTRL_3_H_TGL;
722 	if (mode->flags & VID_NVSYNC)
723 		reg |= VIP_CNTRL_3_V_TGL;
724 	nxphdmi_write(sc, TDA_VIP_CNTRL_3, reg);
725 
726 	reg = TBG_CNTRL_1_TGL_EN;
727 	if (mode->flags & VID_NHSYNC)
728 		reg |= TBG_CNTRL_1_H_TGL;
729 	if (mode->flags & VID_NVSYNC)
730 		reg |= TBG_CNTRL_1_V_TGL;
731 	nxphdmi_write(sc, TDA_TBG_CNTRL_1, reg);
732 
733 	/*  Program timing */
734 	nxphdmi_write(sc, TDA_VIDFORMAT, 0x00);
735 
736 	nxphdmi_write2(sc, TDA_REFPIX_MSB, ref_pix);
737 	nxphdmi_write2(sc, TDA_REFLINE_MSB, ref_line);
738 	nxphdmi_write2(sc, TDA_NPIX_MSB, n_pix);
739 	nxphdmi_write2(sc, TDA_NLINE_MSB, n_line);
740 
741 	nxphdmi_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start);
742 	nxphdmi_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start);
743 	nxphdmi_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end);
744 	nxphdmi_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop);
745 	nxphdmi_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start);
746 	nxphdmi_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start);
747 	nxphdmi_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end);
748 	nxphdmi_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop);
749 	nxphdmi_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start);
750 	nxphdmi_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop);
751 	nxphdmi_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start);
752 	nxphdmi_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end);
753 	nxphdmi_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start);
754 	nxphdmi_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end);
755 	nxphdmi_write2(sc, TDA_DE_START_MSB, de_start);
756 	nxphdmi_write2(sc, TDA_DE_STOP_MSB, de_stop);
757 
758 	nxphdmi_write(sc, TDA_ENABLE_SPACE, 0x00);
759 
760 	/*  must be last register set */
761 	nxphdmi_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
762 
763 	return ret;
764 }
765 
766 int
767 nxphdmi_get_edid(uint8_t *buf, int buflen)
768 {
769 	int ret = 0, i;
770 	struct nxphdmi_softc *sc = nxphdmi_cd.cd_devs[0];
771 
772 	if (buflen < EDID_LENGTH)
773 		return -1;
774 
775 	for (i = 0; i < EDID_LENGTH; i++)
776 		buf[i] = sc->sc_edid[i];
777 
778 	return ret;
779 }
780 
781 int
782 nxphdmi_set_videomode(struct videomode *mode)
783 {
784 	int ret = 0;
785 	struct nxphdmi_softc *sc = nxphdmi_cd.cd_devs[0];
786 
787 	ret = nxphdmi_init_encoder(sc, mode);
788 
789 	return ret;
790 }
791