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