xref: /rk3399_rockchip-uboot/board/gdsys/common/osd.c (revision aba27acf6711dce0ef1507f2f9f02a80d70a45da)
1 /*
2  * (C) Copyright 2010
3  * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <asm/io.h>
10 #include <i2c.h>
11 
12 #include <gdsys_fpga.h>
13 
14 #define CH7301_I2C_ADDR 0x75
15 
16 #define ICS8N3QV01_I2C_ADDR 0x6E
17 #define ICS8N3QV01_FREF 114285000
18 #define ICS8N3QV01_FREF_LL 114285000LL
19 #define ICS8N3QV01_F_DEFAULT_0 156250000LL
20 #define ICS8N3QV01_F_DEFAULT_1 125000000LL
21 #define ICS8N3QV01_F_DEFAULT_2 100000000LL
22 #define ICS8N3QV01_F_DEFAULT_3  25175000LL
23 
24 #define SIL1178_MASTER_I2C_ADDRESS 0x38
25 #define SIL1178_SLAVE_I2C_ADDRESS 0x39
26 
27 #define PIXCLK_640_480_60 25180000
28 
29 #define BASE_WIDTH 32
30 #define BASE_HEIGHT 16
31 #define BUFSIZE (BASE_WIDTH * BASE_HEIGHT)
32 
33 enum {
34 	CH7301_CM = 0x1c,		/* Clock Mode Register */
35 	CH7301_IC = 0x1d,		/* Input Clock Register */
36 	CH7301_GPIO = 0x1e,		/* GPIO Control Register */
37 	CH7301_IDF = 0x1f,		/* Input Data Format Register */
38 	CH7301_CD = 0x20,		/* Connection Detect Register */
39 	CH7301_DC = 0x21,		/* DAC Control Register */
40 	CH7301_HPD = 0x23,		/* Hot Plug Detection Register */
41 	CH7301_TCTL = 0x31,		/* DVI Control Input Register */
42 	CH7301_TPCP = 0x33,		/* DVI PLL Charge Pump Ctrl Register */
43 	CH7301_TPD = 0x34,		/* DVI PLL Divide Register */
44 	CH7301_TPVT = 0x35,		/* DVI PLL Supply Control Register */
45 	CH7301_TPF = 0x36,		/* DVI PLL Filter Register */
46 	CH7301_TCT = 0x37,		/* DVI Clock Test Register */
47 	CH7301_TSTP = 0x48,		/* Test Pattern Register */
48 	CH7301_PM = 0x49,		/* Power Management register */
49 	CH7301_VID = 0x4a,		/* Version ID Register */
50 	CH7301_DID = 0x4b,		/* Device ID Register */
51 	CH7301_DSP = 0x56,		/* DVI Sync polarity Register */
52 };
53 
54 #if defined(CONFIG_SYS_ICS8N3QV01) || defined(CONFIG_SYS_SIL1178)
55 static void fpga_iic_write(unsigned screen, u8 slave, u8 reg, u8 data)
56 {
57 	u16 val;
58 
59 	do {
60 		FPGA_GET_REG(screen, extended_interrupt, &val);
61 	} while (val & (1 << 12));
62 
63 	FPGA_SET_REG(screen, i2c.write_mailbox_ext, reg | (data << 8));
64 	FPGA_SET_REG(screen, i2c.write_mailbox, 0xc400 | (slave << 1));
65 }
66 
67 static u8 fpga_iic_read(unsigned screen, u8 slave, u8 reg)
68 {
69 	unsigned int ctr = 0;
70 	u16 val;
71 
72 	do {
73 		FPGA_GET_REG(screen, extended_interrupt, &val);
74 	} while (val & (1 << 12));
75 
76 	FPGA_SET_REG(screen, extended_interrupt, 1 << 14);
77 	FPGA_SET_REG(screen, i2c.write_mailbox_ext, reg);
78 	FPGA_SET_REG(screen, i2c.write_mailbox, 0xc000 | (slave << 1));
79 
80 	FPGA_GET_REG(screen, extended_interrupt, &val);
81 	while (!(val & (1 << 14))) {
82 		udelay(100000);
83 		if (ctr++ > 5) {
84 			printf("iic receive timeout\n");
85 			break;
86 		}
87 		FPGA_GET_REG(screen, extended_interrupt, &val);
88 	}
89 
90 	FPGA_GET_REG(screen, i2c.read_mailbox_ext, &val);
91 	return val >> 8;
92 }
93 #endif
94 
95 #ifdef CONFIG_SYS_MPC92469AC
96 static void mpc92469ac_calc_parameters(unsigned int fout,
97 	unsigned int *post_div, unsigned int *feedback_div)
98 {
99 	unsigned int n = *post_div;
100 	unsigned int m = *feedback_div;
101 	unsigned int a;
102 	unsigned int b = 14745600 / 16;
103 
104 	if (fout < 50169600)
105 		n = 8;
106 	else if (fout < 100339199)
107 		n = 4;
108 	else if (fout < 200678399)
109 		n = 2;
110 	else
111 		n = 1;
112 
113 	a = fout * n + (b / 2); /* add b/2 for proper rounding */
114 
115 	m = a / b;
116 
117 	*post_div = n;
118 	*feedback_div = m;
119 }
120 
121 static void mpc92469ac_set(unsigned screen, unsigned int fout)
122 {
123 	unsigned int n;
124 	unsigned int m;
125 	unsigned int bitval = 0;
126 	mpc92469ac_calc_parameters(fout, &n, &m);
127 
128 	switch (n) {
129 	case 1:
130 		bitval = 0x00;
131 		break;
132 	case 2:
133 		bitval = 0x01;
134 		break;
135 	case 4:
136 		bitval = 0x02;
137 		break;
138 	case 8:
139 		bitval = 0x03;
140 		break;
141 	}
142 
143 	FPGA_SET_REG(screen, mpc3w_control, (bitval << 9) | m);
144 }
145 #endif
146 
147 #ifdef CONFIG_SYS_ICS8N3QV01
148 
149 static unsigned int ics8n3qv01_get_fout_calc(unsigned screen, unsigned index)
150 {
151 	unsigned long long n;
152 	unsigned long long mint;
153 	unsigned long long mfrac;
154 	u8 reg_a, reg_b, reg_c, reg_d, reg_f;
155 	unsigned long long fout_calc;
156 
157 	if (index > 3)
158 		return 0;
159 
160 	reg_a = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0 + index);
161 	reg_b = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 4 + index);
162 	reg_c = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 8 + index);
163 	reg_d = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 12 + index);
164 	reg_f = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20 + index);
165 
166 	mint = ((reg_a >> 1) & 0x1f) | (reg_f & 0x20);
167 	mfrac = ((reg_a & 0x01) << 17) | (reg_b << 9) | (reg_c << 1)
168 		| (reg_d >> 7);
169 	n = reg_d & 0x7f;
170 
171 	fout_calc = (mint * ICS8N3QV01_FREF_LL
172 		     + mfrac * ICS8N3QV01_FREF_LL / 262144LL
173 		     + ICS8N3QV01_FREF_LL / 524288LL
174 		     + n / 2)
175 		    / n
176 		    * 1000000
177 		    / (1000000 - 100);
178 
179 	return fout_calc;
180 }
181 
182 
183 static void ics8n3qv01_calc_parameters(unsigned int fout,
184 	unsigned int *_mint, unsigned int *_mfrac,
185 	unsigned int *_n)
186 {
187 	unsigned int n;
188 	unsigned int foutiic;
189 	unsigned int fvcoiic;
190 	unsigned int mint;
191 	unsigned long long mfrac;
192 
193 	n = (2215000000U + fout / 2) / fout;
194 	if ((n & 1) && (n > 5))
195 		n -= 1;
196 
197 	foutiic = fout - (fout / 10000);
198 	fvcoiic = foutiic * n;
199 
200 	mint = fvcoiic / 114285000;
201 	if ((mint < 17) || (mint > 63))
202 		printf("ics8n3qv01_calc_parameters: cannot determine mint\n");
203 
204 	mfrac = ((unsigned long long)fvcoiic % 114285000LL) * 262144LL
205 		/ 114285000LL;
206 
207 	*_mint = mint;
208 	*_mfrac = mfrac;
209 	*_n = n;
210 }
211 
212 static void ics8n3qv01_set(unsigned screen, unsigned int fout)
213 {
214 	unsigned int n;
215 	unsigned int mint;
216 	unsigned int mfrac;
217 	unsigned int fout_calc;
218 	unsigned long long fout_prog;
219 	long long off_ppm;
220 	u8 reg0, reg4, reg8, reg12, reg18, reg20;
221 
222 	fout_calc = ics8n3qv01_get_fout_calc(screen, 1);
223 	off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000
224 		  / ICS8N3QV01_F_DEFAULT_1;
225 	printf("       PLL is off by %lld ppm\n", off_ppm);
226 	fout_prog = (unsigned long long)fout * (unsigned long long)fout_calc
227 		    / ICS8N3QV01_F_DEFAULT_1;
228 	ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n);
229 
230 	reg0 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0) & 0xc0;
231 	reg0 |= (mint & 0x1f) << 1;
232 	reg0 |= (mfrac >> 17) & 0x01;
233 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 0, reg0);
234 
235 	reg4 = mfrac >> 9;
236 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 4, reg4);
237 
238 	reg8 = mfrac >> 1;
239 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 8, reg8);
240 
241 	reg12 = mfrac << 7;
242 	reg12 |= n & 0x7f;
243 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 12, reg12);
244 
245 	reg18 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 18) & 0x03;
246 	reg18 |= 0x20;
247 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 18, reg18);
248 
249 	reg20 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20) & 0x1f;
250 	reg20 |= mint & (1 << 5);
251 	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 20, reg20);
252 }
253 #endif
254 
255 static int osd_write_videomem(unsigned screen, unsigned offset,
256 	u16 *data, size_t charcount)
257 {
258 	unsigned int k;
259 
260 	for (k = 0; k < charcount; ++k) {
261 		if (offset + k >= BUFSIZE)
262 			return -1;
263 		FPGA_SET_REG(screen, videomem[offset + k], data[k]);
264 	}
265 
266 	return charcount;
267 }
268 
269 static int osd_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
270 {
271 	unsigned screen;
272 
273 	for (screen = 0; screen < CONFIG_SYS_OSD_SCREENS; ++screen) {
274 		unsigned x;
275 		unsigned y;
276 		unsigned charcount;
277 		unsigned len;
278 		u8 color;
279 		unsigned int k;
280 		u16 buf[BUFSIZE];
281 		char *text;
282 		int res;
283 
284 		if (argc < 5) {
285 			cmd_usage(cmdtp);
286 			return 1;
287 		}
288 
289 		x = simple_strtoul(argv[1], NULL, 16);
290 		y = simple_strtoul(argv[2], NULL, 16);
291 		color = simple_strtoul(argv[3], NULL, 16);
292 		text = argv[4];
293 		charcount = strlen(text);
294 		len = (charcount > BUFSIZE) ? BUFSIZE : charcount;
295 
296 		for (k = 0; k < len; ++k)
297 			buf[k] = (text[k] << 8) | color;
298 
299 		res = osd_write_videomem(screen, y * BASE_WIDTH + x, buf, len);
300 		if (res < 0)
301 			return res;
302 	}
303 
304 	return 0;
305 }
306 
307 int osd_probe(unsigned screen)
308 {
309 	u16 version;
310 	u16 features;
311 	unsigned width;
312 	unsigned height;
313 	u8 value;
314 
315 	FPGA_GET_REG(0, osd.version, &version);
316 	FPGA_GET_REG(0, osd.features, &features);
317 
318 	width = ((features & 0x3f00) >> 8) + 1;
319 	height = (features & 0x001f) + 1;
320 
321 	printf("OSD%d:  Digital-OSD version %01d.%02d, %d" "x%d characters\n",
322 		screen, version/100, version%100, width, height);
323 
324 #ifdef CONFIG_SYS_CH7301
325 	value = i2c_reg_read(CH7301_I2C_ADDR, CH7301_DID);
326 	if (value != 0x17) {
327 		printf("       Probing CH7301 failed, DID %02x\n", value);
328 		return -1;
329 	}
330 	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPCP, 0x08);
331 	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPD, 0x16);
332 	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPF, 0x60);
333 	i2c_reg_write(CH7301_I2C_ADDR, CH7301_DC, 0x09);
334 	i2c_reg_write(CH7301_I2C_ADDR, CH7301_PM, 0xc0);
335 #endif
336 
337 #ifdef CONFIG_SYS_MPC92469AC
338 	mpc92469ac_set(screen, PIXCLK_640_480_60);
339 #endif
340 
341 #ifdef CONFIG_SYS_ICS8N3QV01
342 	ics8n3qv01_set(screen, PIXCLK_640_480_60);
343 #endif
344 
345 #ifdef CONFIG_SYS_SIL1178
346 	value = fpga_iic_read(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x02);
347 	if (value != 0x06) {
348 		printf("       Probing CH7301 SIL1178, DEV_IDL %02x\n", value);
349 		return -1;
350 	}
351 	/* magic initialization sequence adapted from datasheet */
352 	fpga_iic_write(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x08, 0x36);
353 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x44);
354 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x4c);
355 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0e, 0x10);
356 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0a, 0x80);
357 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x09, 0x30);
358 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0c, 0x89);
359 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0d, 0x60);
360 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x36);
361 	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x37);
362 #endif
363 
364 	FPGA_SET_REG(screen, videocontrol, 0x0002);
365 	FPGA_SET_REG(screen, osd.control, 0x0049);
366 
367 	FPGA_SET_REG(screen, osd.xy_size, ((32 - 1) << 8) | (16 - 1));
368 	FPGA_SET_REG(screen, osd.x_pos, 0x007f);
369 	FPGA_SET_REG(screen, osd.y_pos, 0x005f);
370 
371 
372 	return 0;
373 }
374 
375 int osd_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
376 {
377 	unsigned screen;
378 
379 	for (screen = 0; screen < CONFIG_SYS_OSD_SCREENS; ++screen) {
380 		unsigned x;
381 		unsigned y;
382 		unsigned k;
383 		u16 buffer[BASE_WIDTH];
384 		char *rp;
385 		u16 *wp = buffer;
386 		unsigned count = (argc > 4) ?
387 			simple_strtoul(argv[4], NULL, 16) : 1;
388 
389 		if ((argc < 4) || (strlen(argv[3]) % 4)) {
390 			cmd_usage(cmdtp);
391 			return 1;
392 		}
393 
394 		x = simple_strtoul(argv[1], NULL, 16);
395 		y = simple_strtoul(argv[2], NULL, 16);
396 		rp = argv[3];
397 
398 
399 		while (*rp) {
400 			char substr[5];
401 
402 			memcpy(substr, rp, 4);
403 			substr[4] = 0;
404 			*wp = simple_strtoul(substr, NULL, 16);
405 
406 			rp += 4;
407 			wp++;
408 			if (wp - buffer > BASE_WIDTH)
409 				break;
410 		}
411 
412 		for (k = 0; k < count; ++k) {
413 			unsigned offset =
414 				y * BASE_WIDTH + x + k * (wp - buffer);
415 			osd_write_videomem(screen, offset, buffer,
416 				wp - buffer);
417 		}
418 	}
419 
420 	return 0;
421 }
422 
423 U_BOOT_CMD(
424 	osdw, 5, 0, osd_write,
425 	"write 16-bit hex encoded buffer to osd memory",
426 	"pos_x pos_y buffer count\n"
427 );
428 
429 U_BOOT_CMD(
430 	osdp, 5, 0, osd_print,
431 	"write ASCII buffer to osd memory",
432 	"pos_x pos_y color text\n"
433 );
434