X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=board%2Fgdsys%2Fcommon%2Fosd.c;h=392d0059da8fac16dfa48b3525d8ad9885e781b3;hb=83d290c56fab2d38cd1ab4c4cc7099559c1d5046;hp=05800ffba487bb2b895eabc13030b7ce29ccf38d;hpb=17dd883c5b76bdade0f7a48f2eb02d918a5ebef9;p=u-boot diff --git a/board/gdsys/common/osd.c b/board/gdsys/common/osd.c index 05800ffba4..392d0059da 100644 --- a/board/gdsys/common/osd.c +++ b/board/gdsys/common/osd.c @@ -1,72 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2010 - * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc */ #include #include -#include +#include -#include "fpga.h" +#include "ch7301.h" +#include "dp501.h" +#include -#define CH7301_I2C_ADDR 0x75 +#define ICS8N3QV01_I2C_ADDR 0x6E +#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 -#define PIXCLK_640_480_60 25180000 - -#define BASE_WIDTH 32 -#define BASE_HEIGHT 16 -#define BUFSIZE (BASE_WIDTH * BASE_HEIGHT) - -enum { - REG_CONTROL = 0x0010, - REG_MPC3W_CONTROL = 0x001a, - REG_VIDEOCONTROL = 0x0042, - REG_OSDVERSION = 0x0100, - REG_OSDFEATURES = 0x0102, - REG_OSDCONTROL = 0x0104, - REG_XY_SIZE = 0x0106, - REG_VIDEOMEM = 0x0800, -}; - -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 */ -}; +#define SIL1178_MASTER_I2C_ADDRESS 0x38 +#define SIL1178_SLAVE_I2C_ADDRESS 0x39 +#define PIXCLK_640_480_60 25180000 +#define MAX_X_CHARS 53 +#define MAX_Y_CHARS 26 + +#ifdef CONFIG_SYS_OSD_DH +#define MAX_OSD_SCREEN 8 +#define OSD_DH_BASE 4 +#else +#define MAX_OSD_SCREEN 4 +#endif + +#ifdef CONFIG_SYS_OSD_DH +#define OSD_SET_REG(screen, fld, val) \ + do { \ + if (screen >= OSD_DH_BASE) \ + FPGA_SET_REG(screen - OSD_DH_BASE, osd1.fld, val); \ + else \ + FPGA_SET_REG(screen, osd0.fld, val); \ + } while (0) +#else +#define OSD_SET_REG(screen, fld, val) \ + FPGA_SET_REG(screen, osd0.fld, val) +#endif + +#ifdef CONFIG_SYS_OSD_DH +#define OSD_GET_REG(screen, fld, val) \ + do { \ + if (screen >= OSD_DH_BASE) \ + FPGA_GET_REG(screen - OSD_DH_BASE, osd1.fld, val); \ + else \ + FPGA_GET_REG(screen, osd0.fld, val); \ + } while (0) +#else +#define OSD_GET_REG(screen, fld, val) \ + FPGA_GET_REG(screen, osd0.fld, val) +#endif + +unsigned int base_width; +unsigned int base_height; +size_t bufsize; +u16 *buf; + +unsigned int osd_screen_mask = 0; + +#ifdef CONFIG_SYS_ICS8N3QV01_I2C +int ics8n3qv01_i2c[] = CONFIG_SYS_ICS8N3QV01_I2C; +#endif + +#ifdef CONFIG_SYS_SIL1178_I2C +int sil1178_i2c[] = CONFIG_SYS_SIL1178_I2C; +#endif + +#ifdef CONFIG_SYS_MPC92469AC static void mpc92469ac_calc_parameters(unsigned int fout, unsigned int *post_div, unsigned int *feedback_div) { @@ -92,7 +101,7 @@ static void mpc92469ac_calc_parameters(unsigned int fout, *feedback_div = m; } -static void mpc92469ac_set(unsigned int fout) +static void mpc92469ac_set(unsigned screen, unsigned int fout) { unsigned int n; unsigned int m; @@ -114,17 +123,135 @@ static void mpc92469ac_set(unsigned int fout) break; } - fpga_set_reg(REG_MPC3W_CONTROL, (bitval << 9) | m); + FPGA_SET_REG(screen, mpc3w_control, (bitval << 9) | m); +} +#endif + +#ifdef CONFIG_SYS_ICS8N3QV01_I2C + +static unsigned int ics8n3qv01_get_fout_calc(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 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 0 + index); + reg_b = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 4 + index); + reg_c = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 8 + index); + reg_d = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 12 + index); + reg_f = i2c_reg_read(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; } -static int osd_write_videomem(unsigned offset, u16 *data, size_t charcount) + +static void ics8n3qv01_calc_parameters(unsigned int fout, + unsigned int *_mint, unsigned int *_mfrac, + unsigned int *_n) +{ + unsigned int n; + unsigned int foutiic; + unsigned int fvcoiic; + unsigned int mint; + unsigned long long mfrac; + + n = (2215000000U + fout / 2) / fout; + 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 int fout) +{ + unsigned int n; + unsigned int mint; + unsigned int mfrac; + unsigned int fout_calc; + unsigned long long fout_prog; + long long off_ppm; + u8 reg0, reg4, reg8, reg12, reg18, reg20; + + fout_calc = ics8n3qv01_get_fout_calc(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); + + reg0 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 0) & 0xc0; + reg0 |= (mint & 0x1f) << 1; + reg0 |= (mfrac >> 17) & 0x01; + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 0, reg0); + + reg4 = mfrac >> 9; + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 4, reg4); + + reg8 = mfrac >> 1; + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 8, reg8); + + reg12 = mfrac << 7; + reg12 |= n & 0x7f; + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 12, reg12); + + reg18 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 18) & 0x03; + reg18 |= 0x20; + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 18, reg18); + + reg20 = i2c_reg_read(ICS8N3QV01_I2C_ADDR, 20) & 0x1f; + reg20 |= mint & (1 << 5); + i2c_reg_write(ICS8N3QV01_I2C_ADDR, 20, reg20); +} +#endif + +static int osd_write_videomem(unsigned screen, unsigned offset, + u16 *data, size_t charcount) { unsigned int k; for (k = 0; k < charcount; ++k) { - if (offset + k >= BUFSIZE) + if (offset + k >= bufsize) return -1; - fpga_set_reg(REG_VIDEOMEM + 2 * (offset + k), data[k]); +#ifdef CONFIG_SYS_OSD_DH + if (screen >= OSD_DH_BASE) + FPGA_SET_REG(screen - OSD_DH_BASE, + videomem1[offset + k], data[k]); + else + FPGA_SET_REG(screen, videomem0[offset + k], data[k]); +#else + FPGA_SET_REG(screen, videomem0[offset + k], data[k]); +#endif } return charcount; @@ -132,103 +259,221 @@ static int osd_write_videomem(unsigned offset, u16 *data, size_t charcount) static int osd_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - unsigned x; - unsigned y; - unsigned charcount; - unsigned len; - u8 color; - unsigned int k; - u16 buf[BUFSIZE]; - char *text; + unsigned screen; 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); - len = (charcount > BUFSIZE) ? BUFSIZE : charcount; - - for (k = 0; k < len; ++k) - buf[k] = (text[k] << 8) | color; + for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) { + unsigned x; + unsigned y; + unsigned charcount; + unsigned len; + u8 color; + unsigned int k; + char *text; + int res; + + if (!(osd_screen_mask & (1 << screen))) + continue; + + 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); + len = (charcount > bufsize) ? bufsize : charcount; + + for (k = 0; k < len; ++k) + buf[k] = (text[k] << 8) | color; + + res = osd_write_videomem(screen, y * base_width + x, buf, len); + if (res < 0) + return res; + + OSD_SET_REG(screen, control, 0x0049); + } - return osd_write_videomem(y * BASE_WIDTH + x, buf, len); + return 0; } -int osd_probe(void) +int osd_probe(unsigned screen) { - u8 value; - u16 version = fpga_get_reg(REG_OSDVERSION); - u16 features = fpga_get_reg(REG_OSDFEATURES); - unsigned width; - unsigned height; + u16 version; + u16 features; + int old_bus = i2c_get_bus_num(); + bool pixclock_present = false; + bool output_driver_present = false; + + OSD_GET_REG(0, version, &version); + OSD_GET_REG(0, features, &features); + + 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; - width = ((features & 0x3f00) >> 8) + 1; - height = (features & 0x001f) + 1; +#ifdef CONFIG_SYS_OSD_DH + printf("OSD%d-%d: Digital-OSD version %01d.%02d, %d" "x%d characters\n", + (screen >= OSD_DH_BASE) ? (screen - OSD_DH_BASE) : screen, + (screen > 3) ? 1 : 0, version/100, version%100, base_width, + base_height); +#else + printf("OSD%d: Digital-OSD version %01d.%02d, %d" "x%d characters\n", + screen, version/100, version%100, base_width, base_height); +#endif + /* setup pixclock */ + +#ifdef CONFIG_SYS_MPC92469AC + pixclock_present = true; + mpc92469ac_set(screen, PIXCLK_640_480_60); +#endif + +#ifdef CONFIG_SYS_ICS8N3QV01_I2C + i2c_set_bus_num(ics8n3qv01_i2c[screen]); + if (!i2c_probe(ICS8N3QV01_I2C_ADDR)) { + ics8n3qv01_set(PIXCLK_640_480_60); + pixclock_present = true; + } +#endif + + if (!pixclock_present) + printf(" no pixelclock found\n"); + + /* setup output driver */ + +#ifdef CONFIG_SYS_CH7301_I2C + if (!ch7301_probe(screen, true)) + output_driver_present = true; +#endif + +#ifdef CONFIG_SYS_SIL1178_I2C + i2c_set_bus_num(sil1178_i2c[screen]); + if (!i2c_probe(SIL1178_SLAVE_I2C_ADDRESS)) { + if (i2c_reg_read(SIL1178_SLAVE_I2C_ADDRESS, 0x02) == 0x06) { + /* + * magic initialization sequence, + * adapted from datasheet + */ + i2c_reg_write(SIL1178_SLAVE_I2C_ADDRESS, 0x08, 0x36); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x44); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x4c); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0e, 0x10); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0a, 0x80); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x09, 0x30); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0c, 0x89); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x0d, 0x60); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x36); + i2c_reg_write(SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x37); + output_driver_present = true; + } + } +#endif - printf("OSD: Digital-OSD version %01d.%02d, %d" "x%d characters\n", - version/100, version%100, width, height); +#ifdef CONFIG_SYS_DP501_I2C + if (!dp501_probe(screen, true)) + output_driver_present = true; +#endif - value = i2c_reg_read(CH7301_I2C_ADDR, CH7301_DID); - if (value != 0x17) { - printf(" Probing CH7301 failed, DID %02x\n", value); - 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); + if (!output_driver_present) + printf(" no output driver found\n"); + + OSD_SET_REG(screen, xy_size, ((32 - 1) << 8) | (16 - 1)); + OSD_SET_REG(screen, x_pos, 0x007f); + OSD_SET_REG(screen, y_pos, 0x005f); - mpc92469ac_set(PIXCLK_640_480_60); - fpga_set_reg(REG_VIDEOCONTROL, 0x0002); - fpga_set_reg(REG_OSDCONTROL, 0x0049); + if (pixclock_present && output_driver_present) + osd_screen_mask |= 1 << screen; - fpga_set_reg(REG_XY_SIZE, ((32 - 1) << 8) | (16 - 1)); + i2c_set_bus_num(old_bus); return 0; } int osd_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - unsigned x; - unsigned y; - unsigned k; - u16 buffer[BASE_WIDTH]; - char *rp; - u16 *wp = buffer; - unsigned count = (argc > 4) ? simple_strtoul(argv[4], NULL, 16) : 1; + unsigned screen; 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]; + for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) { + unsigned x; + unsigned y; + unsigned k; + u16 buffer[base_width]; + char *rp; + u16 *wp = buffer; + unsigned count = (argc > 4) ? + simple_strtoul(argv[4], NULL, 16) : 1; + + if (!(osd_screen_mask & (1 << screen))) + continue; + + 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++; + if (wp - buffer > base_width) + break; + } + + for (k = 0; k < count; ++k) { + unsigned offset = + y * base_width + x + k * (wp - buffer); + osd_write_videomem(screen, offset, buffer, + wp - buffer); + } + + OSD_SET_REG(screen, control, 0x0049); + } + + return 0; +} +int osd_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned screen; + unsigned x; + unsigned y; - while (*rp) { - char substr[5]; + if (argc < 3) { + cmd_usage(cmdtp); + return 1; + } - memcpy(substr, rp, 4); - substr[4] = 0; - *wp = simple_strtoul(substr, NULL, 16); + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); - rp += 4; - wp++; - if (wp - buffer > BASE_WIDTH) - break; + if (!x || (x > 64) || (x > MAX_X_CHARS) || + !y || (y > 32) || (y > MAX_Y_CHARS)) { + cmd_usage(cmdtp); + return 1; } - for (k = 0; k < count; ++k) { - unsigned offset = y * BASE_WIDTH + x + k * (wp - buffer); - osd_write_videomem(offset, buffer, wp - buffer); + for (screen = 0; screen < MAX_OSD_SCREEN; ++screen) { + if (!(osd_screen_mask & (1 << screen))) + continue; + + OSD_SET_REG(screen, xy_size, ((x - 1) << 8) | (y - 1)); + OSD_SET_REG(screen, x_pos, 32767 * (640 - 12 * x) / 65535); + OSD_SET_REG(screen, y_pos, 32767 * (480 - 18 * y) / 65535); } return 0; @@ -245,3 +490,10 @@ U_BOOT_CMD( "write ASCII buffer to osd memory", "pos_x pos_y color text\n" ); + +U_BOOT_CMD( + osdsize, 3, 0, osd_size, + "set OSD XY size in characters", + "size_x(max. " __stringify(MAX_X_CHARS) + ") size_y(max. " __stringify(MAX_Y_CHARS) ")\n" +);