uboot/board/gdsys/common/osd.c
<<
>>
Prefs
   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 "ch7301.h"
  13#include "dp501.h"
  14#include <gdsys_fpga.h>
  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#define MAX_X_CHARS 53
  29#define MAX_Y_CHARS 26
  30
  31#ifdef CONFIG_SYS_OSD_DH
  32#define MAX_OSD_SCREEN 8
  33#define OSD_DH_BASE 4
  34#else
  35#define MAX_OSD_SCREEN 4
  36#endif
  37
  38#ifdef CONFIG_SYS_OSD_DH
  39#define OSD_SET_REG(screen, fld, val) \
  40        do { \
  41                if (screen >= OSD_DH_BASE) \
  42                        FPGA_SET_REG(screen - OSD_DH_BASE, osd1.fld, val); \
  43                else \
  44                        FPGA_SET_REG(screen, osd0.fld, val); \
  45        } while (0)
  46#else
  47#define OSD_SET_REG(screen, fld, val) \
  48                FPGA_SET_REG(screen, osd0.fld, val)
  49#endif
  50
  51#ifdef CONFIG_SYS_OSD_DH
  52#define OSD_GET_REG(screen, fld, val) \
  53        do {                                    \
  54                if (screen >= OSD_DH_BASE) \
  55                        FPGA_GET_REG(screen - OSD_DH_BASE, osd1.fld, val); \
  56                else \
  57                        FPGA_GET_REG(screen, osd0.fld, val); \
  58        } while (0)
  59#else
  60#define OSD_GET_REG(screen, fld, val) \
  61                FPGA_GET_REG(screen, osd0.fld, val)
  62#endif
  63
  64unsigned int base_width;
  65unsigned int base_height;
  66size_t bufsize;
  67u16 *buf;
  68
  69unsigned int osd_screen_mask = 0;
  70
  71#ifdef CONFIG_SYS_ICS8N3QV01_I2C
  72int ics8n3qv01_i2c[] = CONFIG_SYS_ICS8N3QV01_I2C;
  73#endif
  74
  75#ifdef CONFIG_SYS_SIL1178_I2C
  76int sil1178_i2c[] = CONFIG_SYS_SIL1178_I2C;
  77#endif
  78
  79#ifdef CONFIG_SYS_MPC92469AC
  80static void mpc92469ac_calc_parameters(unsigned int fout,
  81        unsigned int *post_div, unsigned int *feedback_div)
  82{
  83        unsigned int n = *post_div;
  84        unsigned int m = *feedback_div;
  85        unsigned int a;
  86        unsigned int b = 14745600 / 16;
  87
  88        if (fout < 50169600)
  89                n = 8;
  90        else if (fout < 100339199)
  91                n = 4;
  92        else if (fout < 200678399)
  93                n = 2;
  94        else
  95                n = 1;
  96
  97        a = fout * n + (b / 2); /* add b/2 for proper rounding */
  98
  99        m = a / b;
 100
 101        *post_div = n;
 102        *feedback_div = m;
 103}
 104
 105static void mpc92469ac_set(unsigned screen, unsigned int fout)
 106{
 107        unsigned int n;
 108        unsigned int m;
 109        unsigned int bitval = 0;
 110        mpc92469ac_calc_parameters(fout, &n, &m);
 111
 112        switch (n) {
 113        case 1:
 114                bitval = 0x00;
 115                break;
 116        case 2:
 117                bitval = 0x01;
 118                break;
 119        case 4:
 120                bitval = 0x02;
 121                break;
 122        case 8:
 123                bitval = 0x03;
 124                break;
 125        }
 126
 127        FPGA_SET_REG(screen, mpc3w_control, (bitval << 9) | m);
 128}
 129#endif
 130
 131#ifdef CONFIG_SYS_ICS8N3QV01_I2C
 132
 133static unsigned int ics8n3qv01_get_fout_calc(unsigned index)
 134{
 135        unsigned long long n;
 136        unsigned long long mint;
 137        unsigned long long mfrac;
 138        u8 reg_a, reg_b, reg_c, reg_d, reg_f;
 139        unsigned long long fout_calc;
 140
 141        if (index > 3)
 142                return 0;
 143
 144        reg_a = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 0 + index);
 145        reg_b = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 4 + index);
 146        reg_c = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 8 + index);
 147        reg_d = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 12 + index);
 148        reg_f = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 20 + index);
 149
 150        mint = ((reg_a >> 1) & 0x1f) | (reg_f & 0x20);
 151        mfrac = ((reg_a & 0x01) << 17) | (reg_b << 9) | (reg_c << 1)
 152                | (reg_d >> 7);
 153        n = reg_d & 0x7f;
 154
 155        fout_calc = (mint * ICS8N3QV01_FREF_LL
 156                     + mfrac * ICS8N3QV01_FREF_LL / 262144LL
 157                     + ICS8N3QV01_FREF_LL / 524288LL
 158                     + n / 2)
 159                    / n
 160                    * 1000000
 161                    / (1000000 - 100);
 162
 163        return fout_calc;
 164}
 165
 166
 167static void ics8n3qv01_calc_parameters(unsigned int fout,
 168        unsigned int *_mint, unsigned int *_mfrac,
 169        unsigned int *_n)
 170{
 171        unsigned int n;
 172        unsigned int foutiic;
 173        unsigned int fvcoiic;
 174        unsigned int mint;
 175        unsigned long long mfrac;
 176
 177        n = (2215000000U + fout / 2) / fout;
 178        if ((n & 1) && (n > 5))
 179                n -= 1;
 180
 181        foutiic = fout - (fout / 10000);
 182        fvcoiic = foutiic * n;
 183
 184        mint = fvcoiic / 114285000;
 185        if ((mint < 17) || (mint > 63))
 186                printf("ics8n3qv01_calc_parameters: cannot determine mint\n");
 187
 188        mfrac = ((unsigned long long)fvcoiic % 114285000LL) * 262144LL
 189                / 114285000LL;
 190
 191        *_mint = mint;
 192        *_mfrac = mfrac;
 193        *_n = n;
 194}
 195
 196static void ics8n3qv01_set(unsigned int fout)
 197{
 198        unsigned int n;
 199        unsigned int mint;
 200        unsigned int mfrac;
 201        unsigned int fout_calc;
 202        unsigned long long fout_prog;
 203        long long off_ppm;
 204        u8 reg0, reg4, reg8, reg12, reg18, reg20;
 205
 206        fout_calc = ics8n3qv01_get_fout_calc(1);
 207        off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000
 208                  / ICS8N3QV01_F_DEFAULT_1;
 209        printf("       PLL is off by %lld ppm\n", off_ppm);
 210        fout_prog = (unsigned long long)fout * (unsigned long long)fout_calc
 211                    / ICS8N3QV01_F_DEFAULT_1;
 212        ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n);
 213
 214        reg0 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 0) & 0xc0;
 215        reg0 |= (mint & 0x1f) << 1;
 216        reg0 |= (mfrac >> 17) & 0x01;
 217        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 0, reg0);
 218
 219        reg4 = mfrac >> 9;
 220        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 4, reg4);
 221
 222        reg8 = mfrac >> 1;
 223        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 8, reg8);
 224
 225        reg12 = mfrac << 7;
 226        reg12 |= n & 0x7f;
 227        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 12, reg12);
 228
 229        reg18 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 18) & 0x03;
 230        reg18 |= 0x20;
 231        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 18, reg18);
 232
 233        reg20 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 20) & 0x1f;
 234        reg20 |= mint & (1 << 5);
 235        i2c_reg_write(ICS8N3QV01_I2C_ADDR, 20, reg20);
 236}
 237#endif
 238
 239static int osd_write_videomem(unsigned screen, unsigned offset,
 240        u16 *data, size_t charcount)
 241{
 242        unsigned int k;
 243
 244        for (k = 0; k < charcount; ++k) {
 245                if (offset + k >= bufsize)
 246                        return -1;
 247#ifdef CONFIG_SYS_OSD_DH
 248                if (screen >= OSD_DH_BASE)
 249                        FPGA_SET_REG(screen - OSD_DH_BASE,
 250                                     videomem1[offset + k], data[k]);
 251                else
 252                        FPGA_SET_REG(screen, videomem0[offset + k], data[k]);
 253#else
 254                FPGA_SET_REG(screen, videomem0[offset + k], data[k]);
 255#endif
 256        }
 257
 258        return charcount;
 259}
 260
 261static int osd_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 262{
 263        unsigned screen;
 264
 265        if (argc < 5) {
 266                cmd_usage(cmdtp);
 267                return 1;
 268        }
 269
 270        for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) {
 271                unsigned x;
 272                unsigned y;
 273                unsigned charcount;
 274                unsigned len;
 275                u8 color;
 276                unsigned int k;
 277                char *text;
 278                int res;
 279
 280                if (!(osd_screen_mask & (1 << screen)))
 281                        continue;
 282
 283                x = simple_strtoul(argv[1], NULL, 16);
 284                y = simple_strtoul(argv[2], NULL, 16);
 285                color = simple_strtoul(argv[3], NULL, 16);
 286                text = argv[4];
 287                charcount = strlen(text);
 288                len = (charcount > bufsize) ? bufsize : charcount;
 289
 290                for (k = 0; k < len; ++k)
 291                        buf[k] = (text[k] << 8) | color;
 292
 293                res = osd_write_videomem(screen, y * base_width + x, buf, len);
 294                if (res < 0)
 295                        return res;
 296
 297                OSD_SET_REG(screen, control, 0x0049);
 298        }
 299
 300        return 0;
 301}
 302
 303int osd_probe(unsigned screen)
 304{
 305        u16 version;
 306        u16 features;
 307        int old_bus = i2c_get_bus_num();
 308        bool pixclock_present = false;
 309        bool output_driver_present = false;
 310
 311        OSD_GET_REG(0, version, &version);
 312        OSD_GET_REG(0, features, &features);
 313
 314        base_width = ((features & 0x3f00) >> 8) + 1;
 315        base_height = (features & 0x001f) + 1;
 316        bufsize = base_width * base_height;
 317        buf = malloc(sizeof(u16) * bufsize);
 318        if (!buf)
 319                return -1;
 320
 321#ifdef CONFIG_SYS_OSD_DH
 322        printf("OSD%d-%d: Digital-OSD version %01d.%02d, %d" "x%d characters\n",
 323               (screen >= OSD_DH_BASE) ? (screen - OSD_DH_BASE) : screen,
 324               (screen > 3) ? 1 : 0, version/100, version%100, base_width,
 325               base_height);
 326#else
 327        printf("OSD%d:  Digital-OSD version %01d.%02d, %d" "x%d characters\n",
 328               screen, version/100, version%100, base_width, base_height);
 329#endif
 330        /* setup pixclock */
 331
 332#ifdef CONFIG_SYS_MPC92469AC
 333        pixclock_present = true;
 334        mpc92469ac_set(screen, PIXCLK_640_480_60);
 335#endif
 336
 337#ifdef CONFIG_SYS_ICS8N3QV01_I2C
 338        i2c_set_bus_num(ics8n3qv01_i2c[screen]);
 339        if (!i2c_probe(ICS8N3QV01_I2C_ADDR)) {
 340                ics8n3qv01_set(PIXCLK_640_480_60);
 341                pixclock_present = true;
 342        }
 343#endif
 344
 345        if (!pixclock_present)
 346                printf("       no pixelclock found\n");
 347
 348        /* setup output driver */
 349
 350#ifdef CONFIG_SYS_CH7301_I2C
 351        if (!ch7301_probe(screen, true))
 352                output_driver_present = true;
 353#endif
 354
 355#ifdef CONFIG_SYS_SIL1178_I2C
 356        i2c_set_bus_num(sil1178_i2c[screen]);
 357        if (!i2c_probe(SIL1178_SLAVE_I2C_ADDRESS)) {
 358                if (i2c_reg_read(SIL1178_SLAVE_I2C_ADDRESS, 0x02) == 0x06) {
 359                        /*
 360                         * magic initialization sequence,
 361                         * adapted from datasheet
 362                         */
 363                        i2c_reg_write(SIL1178_SLAVE_I2C_ADDRESS, 0x08, 0x36);
 364                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x44);
 365                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x4c);
 366                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0e, 0x10);
 367                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0a, 0x80);
 368                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x09, 0x30);
 369                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0c, 0x89);
 370                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0d, 0x60);
 371                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x36);
 372                        i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x37);
 373                        output_driver_present = true;
 374                }
 375        }
 376#endif
 377
 378#ifdef CONFIG_SYS_DP501_I2C
 379        if (!dp501_probe(screen, true))
 380                output_driver_present = true;
 381#endif
 382
 383        if (!output_driver_present)
 384                printf("       no output driver found\n");
 385
 386        OSD_SET_REG(screen, xy_size, ((32 - 1) << 8) | (16 - 1));
 387        OSD_SET_REG(screen, x_pos, 0x007f);
 388        OSD_SET_REG(screen, y_pos, 0x005f);
 389
 390        if (pixclock_present && output_driver_present)
 391                osd_screen_mask |= 1 << screen;
 392
 393        i2c_set_bus_num(old_bus);
 394
 395        return 0;
 396}
 397
 398int osd_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 399{
 400        unsigned screen;
 401
 402        if ((argc < 4) || (strlen(argv[3]) % 4)) {
 403                cmd_usage(cmdtp);
 404                return 1;
 405        }
 406
 407        for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) {
 408                unsigned x;
 409                unsigned y;
 410                unsigned k;
 411                u16 buffer[base_width];
 412                char *rp;
 413                u16 *wp = buffer;
 414                unsigned count = (argc > 4) ?
 415                        simple_strtoul(argv[4], NULL, 16) : 1;
 416
 417                if (!(osd_screen_mask & (1 << screen)))
 418                        continue;
 419
 420                x = simple_strtoul(argv[1], NULL, 16);
 421                y = simple_strtoul(argv[2], NULL, 16);
 422                rp = argv[3];
 423
 424
 425                while (*rp) {
 426                        char substr[5];
 427
 428                        memcpy(substr, rp, 4);
 429                        substr[4] = 0;
 430                        *wp = simple_strtoul(substr, NULL, 16);
 431
 432                        rp += 4;
 433                        wp++;
 434                        if (wp - buffer > base_width)
 435                                break;
 436                }
 437
 438                for (k = 0; k < count; ++k) {
 439                        unsigned offset =
 440                                y * base_width + x + k * (wp - buffer);
 441                        osd_write_videomem(screen, offset, buffer,
 442                                wp - buffer);
 443                }
 444
 445                OSD_SET_REG(screen, control, 0x0049);
 446        }
 447
 448        return 0;
 449}
 450
 451int osd_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 452{
 453        unsigned screen;
 454        unsigned x;
 455        unsigned y;
 456
 457        if (argc < 3) {
 458                cmd_usage(cmdtp);
 459                return 1;
 460        }
 461
 462        x = simple_strtoul(argv[1], NULL, 16);
 463        y = simple_strtoul(argv[2], NULL, 16);
 464
 465        if (!x || (x > 64) || (x > MAX_X_CHARS) ||
 466            !y || (y > 32) || (y > MAX_Y_CHARS)) {
 467                cmd_usage(cmdtp);
 468                return 1;
 469        }
 470
 471        for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) {
 472                if (!(osd_screen_mask & (1 << screen)))
 473                        continue;
 474
 475                OSD_SET_REG(screen, xy_size, ((x - 1) << 8) | (y - 1));
 476                OSD_SET_REG(screen, x_pos, 32767 * (640 - 12 * x) / 65535);
 477                OSD_SET_REG(screen, y_pos, 32767 * (480 - 18 * y) / 65535);
 478        }
 479
 480        return 0;
 481}
 482
 483U_BOOT_CMD(
 484        osdw, 5, 0, osd_write,
 485        "write 16-bit hex encoded buffer to osd memory",
 486        "pos_x pos_y buffer count\n"
 487);
 488
 489U_BOOT_CMD(
 490        osdp, 5, 0, osd_print,
 491        "write ASCII buffer to osd memory",
 492        "pos_x pos_y color text\n"
 493);
 494
 495U_BOOT_CMD(
 496        osdsize, 3, 0, osd_size,
 497        "set OSD XY size in characters",
 498        "size_x(max. " __stringify(MAX_X_CHARS)
 499        ") size_y(max. " __stringify(MAX_Y_CHARS) ")\n"
 500);
 501