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