osd.c 10.9 KB
Newer Older
1 2 3 4
/*
 * (C) Copyright 2010
 * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
 *
5
 * SPDX-License-Identifier:	GPL-2.0+
6 7 8
 */

#include <common.h>
9
#include <i2c.h>
10
#include <malloc.h>
11

12
#include <gdsys_fpga.h>
13 14 15

#define CH7301_I2C_ADDR 0x75

16
#define ICS8N3QV01_I2C_ADDR 0x6E
17 18 19 20 21 22
#define ICS8N3QV01_FREF 114285000
#define ICS8N3QV01_FREF_LL 114285000LL
#define ICS8N3QV01_F_DEFAULT_0 156250000LL
#define ICS8N3QV01_F_DEFAULT_1 125000000LL
#define ICS8N3QV01_F_DEFAULT_2 100000000LL
#define ICS8N3QV01_F_DEFAULT_3  25175000LL
23 24 25 26

#define SIL1178_MASTER_I2C_ADDRESS 0x38
#define SIL1178_SLAVE_I2C_ADDRESS 0x39

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
#define PIXCLK_640_480_60 25180000

enum {
	CH7301_CM = 0x1c,		/* Clock Mode Register */
	CH7301_IC = 0x1d,		/* Input Clock Register */
	CH7301_GPIO = 0x1e,		/* GPIO Control Register */
	CH7301_IDF = 0x1f,		/* Input Data Format Register */
	CH7301_CD = 0x20,		/* Connection Detect Register */
	CH7301_DC = 0x21,		/* DAC Control Register */
	CH7301_HPD = 0x23,		/* Hot Plug Detection Register */
	CH7301_TCTL = 0x31,		/* DVI Control Input Register */
	CH7301_TPCP = 0x33,		/* DVI PLL Charge Pump Ctrl Register */
	CH7301_TPD = 0x34,		/* DVI PLL Divide Register */
	CH7301_TPVT = 0x35,		/* DVI PLL Supply Control Register */
	CH7301_TPF = 0x36,		/* DVI PLL Filter Register */
	CH7301_TCT = 0x37,		/* DVI Clock Test Register */
	CH7301_TSTP = 0x48,		/* Test Pattern Register */
	CH7301_PM = 0x49,		/* Power Management register */
	CH7301_VID = 0x4a,		/* Version ID Register */
	CH7301_DID = 0x4b,		/* Device ID Register */
	CH7301_DSP = 0x56,		/* DVI Sync polarity Register */
};

50 51 52 53 54
unsigned int base_width;
unsigned int base_height;
size_t bufsize;
u16 *buf;

55 56 57 58 59 60
unsigned int max_osd_screen = CONFIG_SYS_OSD_SCREENS - 1;

#ifdef CONFIG_SYS_CH7301
int ch7301_i2c[] = CONFIG_SYS_CH7301_I2C;
#endif

61 62 63
#if defined(CONFIG_SYS_ICS8N3QV01) || defined(CONFIG_SYS_SIL1178)
static void fpga_iic_write(unsigned screen, u8 slave, u8 reg, u8 data)
{
64
	u16 val;
65

66 67 68 69 70 71
	do {
		FPGA_GET_REG(screen, extended_interrupt, &val);
	} while (val & (1 << 12));

	FPGA_SET_REG(screen, i2c.write_mailbox_ext, reg | (data << 8));
	FPGA_SET_REG(screen, i2c.write_mailbox, 0xc400 | (slave << 1));
72 73 74 75 76
}

static u8 fpga_iic_read(unsigned screen, u8 slave, u8 reg)
{
	unsigned int ctr = 0;
77 78 79 80 81
	u16 val;

	do {
		FPGA_GET_REG(screen, extended_interrupt, &val);
	} while (val & (1 << 12));
82

83 84 85 86 87 88
	FPGA_SET_REG(screen, extended_interrupt, 1 << 14);
	FPGA_SET_REG(screen, i2c.write_mailbox_ext, reg);
	FPGA_SET_REG(screen, i2c.write_mailbox, 0xc000 | (slave << 1));

	FPGA_GET_REG(screen, extended_interrupt, &val);
	while (!(val & (1 << 14))) {
89 90 91 92 93
		udelay(100000);
		if (ctr++ > 5) {
			printf("iic receive timeout\n");
			break;
		}
94
		FPGA_GET_REG(screen, extended_interrupt, &val);
95
	}
96 97 98

	FPGA_GET_REG(screen, i2c.read_mailbox_ext, &val);
	return val >> 8;
99 100 101 102
}
#endif

#ifdef CONFIG_SYS_MPC92469AC
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static void mpc92469ac_calc_parameters(unsigned int fout,
	unsigned int *post_div, unsigned int *feedback_div)
{
	unsigned int n = *post_div;
	unsigned int m = *feedback_div;
	unsigned int a;
	unsigned int b = 14745600 / 16;

	if (fout < 50169600)
		n = 8;
	else if (fout < 100339199)
		n = 4;
	else if (fout < 200678399)
		n = 2;
	else
		n = 1;

	a = fout * n + (b / 2); /* add b/2 for proper rounding */

	m = a / b;

	*post_div = n;
	*feedback_div = m;
}

128
static void mpc92469ac_set(unsigned screen, unsigned int fout)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
{
	unsigned int n;
	unsigned int m;
	unsigned int bitval = 0;
	mpc92469ac_calc_parameters(fout, &n, &m);

	switch (n) {
	case 1:
		bitval = 0x00;
		break;
	case 2:
		bitval = 0x01;
		break;
	case 4:
		bitval = 0x02;
		break;
	case 8:
		bitval = 0x03;
		break;
	}

150
	FPGA_SET_REG(screen, mpc3w_control, (bitval << 9) | m);
151
}
152
#endif
153

154
#ifdef CONFIG_SYS_ICS8N3QV01
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

static unsigned int ics8n3qv01_get_fout_calc(unsigned screen, unsigned index)
{
	unsigned long long n;
	unsigned long long mint;
	unsigned long long mfrac;
	u8 reg_a, reg_b, reg_c, reg_d, reg_f;
	unsigned long long fout_calc;

	if (index > 3)
		return 0;

	reg_a = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0 + index);
	reg_b = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 4 + index);
	reg_c = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 8 + index);
	reg_d = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 12 + index);
	reg_f = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20 + index);

	mint = ((reg_a >> 1) & 0x1f) | (reg_f & 0x20);
	mfrac = ((reg_a & 0x01) << 17) | (reg_b << 9) | (reg_c << 1)
		| (reg_d >> 7);
	n = reg_d & 0x7f;

	fout_calc = (mint * ICS8N3QV01_FREF_LL
		     + mfrac * ICS8N3QV01_FREF_LL / 262144LL
		     + ICS8N3QV01_FREF_LL / 524288LL
		     + n / 2)
		    / n
		    * 1000000
		    / (1000000 - 100);

	return fout_calc;
}


190 191 192
static void ics8n3qv01_calc_parameters(unsigned int fout,
	unsigned int *_mint, unsigned int *_mfrac,
	unsigned int *_n)
193
{
194 195 196 197 198 199
	unsigned int n;
	unsigned int foutiic;
	unsigned int fvcoiic;
	unsigned int mint;
	unsigned long long mfrac;

200
	n = (2215000000U + fout / 2) / fout;
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	if ((n & 1) && (n > 5))
		n -= 1;

	foutiic = fout - (fout / 10000);
	fvcoiic = foutiic * n;

	mint = fvcoiic / 114285000;
	if ((mint < 17) || (mint > 63))
		printf("ics8n3qv01_calc_parameters: cannot determine mint\n");

	mfrac = ((unsigned long long)fvcoiic % 114285000LL) * 262144LL
		/ 114285000LL;

	*_mint = mint;
	*_mfrac = mfrac;
	*_n = n;
}

static void ics8n3qv01_set(unsigned screen, unsigned int fout)
{
	unsigned int n;
	unsigned int mint;
	unsigned int mfrac;
224 225 226
	unsigned int fout_calc;
	unsigned long long fout_prog;
	long long off_ppm;
227 228
	u8 reg0, reg4, reg8, reg12, reg18, reg20;

229 230 231 232 233 234 235
	fout_calc = ics8n3qv01_get_fout_calc(screen, 1);
	off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000
		  / ICS8N3QV01_F_DEFAULT_1;
	printf("       PLL is off by %lld ppm\n", off_ppm);
	fout_prog = (unsigned long long)fout * (unsigned long long)fout_calc
		    / ICS8N3QV01_F_DEFAULT_1;
	ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

	reg0 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0) & 0xc0;
	reg0 |= (mint & 0x1f) << 1;
	reg0 |= (mfrac >> 17) & 0x01;
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 0, reg0);

	reg4 = mfrac >> 9;
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 4, reg4);

	reg8 = mfrac >> 1;
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 8, reg8);

	reg12 = mfrac << 7;
	reg12 |= n & 0x7f;
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 12, reg12);

	reg18 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 18) & 0x03;
	reg18 |= 0x20;
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 18, reg18);

	reg20 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20) & 0x1f;
	reg20 |= mint & (1 << 5);
	fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 20, reg20);
}
#endif

static int osd_write_videomem(unsigned screen, unsigned offset,
	u16 *data, size_t charcount)
{
265 266 267
	unsigned int k;

	for (k = 0; k < charcount; ++k) {
268
		if (offset + k >= bufsize)
269
			return -1;
270
		FPGA_SET_REG(screen, videomem[offset + k], data[k]);
271 272 273 274 275 276 277
	}

	return charcount;
}

static int osd_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
278 279
	unsigned screen;

280
	for (screen = 0; screen <= max_osd_screen; ++screen) {
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
		unsigned x;
		unsigned y;
		unsigned charcount;
		unsigned len;
		u8 color;
		unsigned int k;
		char *text;
		int res;

		if (argc < 5) {
			cmd_usage(cmdtp);
			return 1;
		}

		x = simple_strtoul(argv[1], NULL, 16);
		y = simple_strtoul(argv[2], NULL, 16);
		color = simple_strtoul(argv[3], NULL, 16);
		text = argv[4];
		charcount = strlen(text);
300
		len = (charcount > bufsize) ? bufsize : charcount;
301 302 303 304

		for (k = 0; k < len; ++k)
			buf[k] = (text[k] << 8) | color;

305
		res = osd_write_videomem(screen, y * base_width + x, buf, len);
306 307
		if (res < 0)
			return res;
308 309
	}

310
	return 0;
311 312
}

313
int osd_probe(unsigned screen)
314
{
315 316
	u16 version;
	u16 features;
317
	u8 value;
318 319 320
#ifdef CONFIG_SYS_CH7301
	int old_bus = i2c_get_bus_num();
#endif
321

322 323 324
	FPGA_GET_REG(0, osd.version, &version);
	FPGA_GET_REG(0, osd.features, &features);

325 326 327 328 329 330
	base_width = ((features & 0x3f00) >> 8) + 1;
	base_height = (features & 0x001f) + 1;
	bufsize = base_width * base_height;
	buf = malloc(sizeof(u16) * bufsize);
	if (!buf)
		return -1;
331

332
	printf("OSD%d:  Digital-OSD version %01d.%02d, %d" "x%d characters\n",
333
		screen, version/100, version%100, base_width, base_height);
334

335
#ifdef CONFIG_SYS_CH7301
336
	i2c_set_bus_num(ch7301_i2c[screen]);
337 338 339
	value = i2c_reg_read(CH7301_I2C_ADDR, CH7301_DID);
	if (value != 0x17) {
		printf("       Probing CH7301 failed, DID %02x\n", value);
340
		i2c_set_bus_num(old_bus);
341 342 343 344 345 346 347
		return -1;
	}
	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPCP, 0x08);
	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPD, 0x16);
	i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPF, 0x60);
	i2c_reg_write(CH7301_I2C_ADDR, CH7301_DC, 0x09);
	i2c_reg_write(CH7301_I2C_ADDR, CH7301_PM, 0xc0);
348
	i2c_set_bus_num(old_bus);
349 350 351 352 353
#endif

#ifdef CONFIG_SYS_MPC92469AC
	mpc92469ac_set(screen, PIXCLK_640_480_60);
#endif
354

355 356 357
#ifdef CONFIG_SYS_ICS8N3QV01
	ics8n3qv01_set(screen, PIXCLK_640_480_60);
#endif
358

359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
#ifdef CONFIG_SYS_SIL1178
	value = fpga_iic_read(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x02);
	if (value != 0x06) {
		printf("       Probing CH7301 SIL1178, DEV_IDL %02x\n", value);
		return -1;
	}
	/* magic initialization sequence adapted from datasheet */
	fpga_iic_write(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x08, 0x36);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x44);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x4c);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0e, 0x10);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0a, 0x80);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x09, 0x30);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0c, 0x89);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0d, 0x60);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x36);
	fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x37);
#endif

378 379 380 381 382 383
	FPGA_SET_REG(screen, videocontrol, 0x0002);
	FPGA_SET_REG(screen, osd.control, 0x0049);

	FPGA_SET_REG(screen, osd.xy_size, ((32 - 1) << 8) | (16 - 1));
	FPGA_SET_REG(screen, osd.x_pos, 0x007f);
	FPGA_SET_REG(screen, osd.y_pos, 0x005f);
384

385 386
	if (screen > max_osd_screen)
		max_osd_screen = screen;
387 388 389 390 391 392

	return 0;
}

int osd_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
393 394
	unsigned screen;

395
	for (screen = 0; screen <= max_osd_screen; ++screen) {
396 397 398
		unsigned x;
		unsigned y;
		unsigned k;
399
		u16 buffer[base_width];
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
		char *rp;
		u16 *wp = buffer;
		unsigned count = (argc > 4) ?
			simple_strtoul(argv[4], NULL, 16) : 1;

		if ((argc < 4) || (strlen(argv[3]) % 4)) {
			cmd_usage(cmdtp);
			return 1;
		}

		x = simple_strtoul(argv[1], NULL, 16);
		y = simple_strtoul(argv[2], NULL, 16);
		rp = argv[3];


		while (*rp) {
			char substr[5];

			memcpy(substr, rp, 4);
			substr[4] = 0;
			*wp = simple_strtoul(substr, NULL, 16);

			rp += 4;
			wp++;
424
			if (wp - buffer > base_width)
425 426 427 428 429
				break;
		}

		for (k = 0; k < count; ++k) {
			unsigned offset =
430
				y * base_width + x + k * (wp - buffer);
431 432 433
			osd_write_videomem(screen, offset, buffer,
				wp - buffer);
		}
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
	}

	return 0;
}

U_BOOT_CMD(
	osdw, 5, 0, osd_write,
	"write 16-bit hex encoded buffer to osd memory",
	"pos_x pos_y buffer count\n"
);

U_BOOT_CMD(
	osdp, 5, 0, osd_print,
	"write ASCII buffer to osd memory",
	"pos_x pos_y color text\n"
);