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