Add a driver for IHS OSDs on IHS FPGAs. Signed-off-by: Mario Six <mario....@gdsys.cc> --- drivers/misc/Kconfig | 6 +- drivers/video/Kconfig | 7 ++ drivers/video/Makefile | 1 + drivers/video/ihs_video_out.c | 277 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 drivers/video/ihs_video_out.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d774569cbc..742fee3b76 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -263,5 +263,9 @@ config SYS_I2C_EEPROM_ADDR_OVERFLOW endif - +config IHS_VIDEO_OUT + bool "Enable IHS video out driver" + depends on MISC + help + Support for IHS video out. endmenu diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index da60bbe692..40a881cf56 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -668,4 +668,11 @@ config OSD This supports drivers that provide a OSD (on-screen display), which is a (usually text-oriented) graphics buffer to show information on a display. + +config IHS_VIDEO_OUT + bool "Enable IHS video out driver" + depends on OSD + help + Support for IHS video out OSD. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 209d5e3a75..0d633e03d4 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -59,6 +59,7 @@ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ obj-${CONFIG_OSD} += video_osd-uclass.o +obj-$(CONFIG_IHS_VIDEO_OUT) += ihs_video_out.o obj-y += bridge/ obj-y += sunxi/ diff --git a/drivers/video/ihs_video_out.c b/drivers/video/ihs_video_out.c new file mode 100644 index 0000000000..3efb343c02 --- /dev/null +++ b/drivers/video/ihs_video_out.c @@ -0,0 +1,277 @@ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario....@gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eib...@gdsys.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <display.h> +#include <dm.h> +#include <fpgamap.h> +#include <video_osd.h> +#include <asm/gpio.h> + +#include "../misc/gdsys_soc.h" +#include "../video/logicore_dp_tx.h" + +#define MAX_X_CHARS 53 +#define MAX_Y_CHARS 26 +#define MAX_VIDEOMEM_WIDTH (2 * 64) +#define MAX_VIDEOMEM_HEIGHT (2 * 32) + +enum { + REG_VERSION = 0x00, + REG_FEATURES = 0x02, + REG_CONTROL = 0x04, + REG_XY_SIZE = 0x06, + REG_XY_SCALE = 0x08, + REG_X_POS = 0x0A, + REG_Y_POS = 0x0C, +}; + +struct ihs_video_out_priv { + int addr; + int osd_base; + int osd_buffer_base; + int osd_buffer_size; + uint base_width; + uint base_height; + uint res_x; + uint res_y; + int sync_src; + struct udevice *dp_tx; + struct udevice *clk_gen; +}; + +/** + * struct ihs_video_out_data - information about a IHS OSD instance + * + * @width Maximum width of the OSD screen in characters. + * @heigth Maximum height of the OSD screen in characters. + */ +struct ihs_video_out_data { + uint width; + uint height; +}; + +static const struct udevice_id ihs_video_out_ids[] = { + { .compatible = "gdsys,ihs_video_out" }, + { } +}; + +static int set_control(struct udevice *dev, u16 value) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + struct udevice *fpga; + + gdsys_soc_get_fpga(dev, &fpga); + + if (priv->sync_src) + value |= ((priv->sync_src & 0x7) << 8); + + fpgamap_write(fpga, priv->osd_base + REG_CONTROL, &value, + FPGAMAP_SIZE_16); + + return 0; +} + +static void print_info(struct udevice *dev) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + struct udevice *fpga; + u16 version; + u16 features; + + gdsys_soc_get_fpga(dev, &fpga); + + fpgamap_read(fpga, priv->osd_base + REG_VERSION, &version, + FPGAMAP_SIZE_16); + fpgamap_read(fpga, priv->osd_base + REG_FEATURES, &features, + FPGAMAP_SIZE_16); + + set_control(dev, 0x0049); + + priv->base_width = ((features & 0x3f00) >> 8) + 1; + priv->base_height = (features & 0x001f) + 1; + + printf("OSD-%s: Digital-OSD version %01d.%02d, %d x %d characters\n", + dev->name, version / 100, version % 100, + priv->base_width, priv->base_height); +} + +int ihs_video_out_get_data(struct udevice *dev, void *data) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + struct ihs_video_out_data *ihs_data = data; + + ihs_data->width = priv->base_width; + ihs_data->height = priv->base_height; + + return 0; +} + +int ihs_video_out_set_mem(struct udevice *dev, uint x, uint y, u8 *buf, + size_t buflen, uint count) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + struct udevice *fpga; + uint offset; + uint k, l; + u16 data; + + gdsys_soc_get_fpga(dev, &fpga); + + for (l = 0; l < count; ++l) { + offset = y * priv->base_width + x + l * (buflen / 2); + + for (k = 0; k < buflen / 2; ++k) { + if (offset + k >= priv->base_width * priv->base_height) + return -ENXIO; + + data = buf[2 * k + 1] + 256 * buf[2 * k]; + fpgamap_write(fpga, + priv->osd_buffer_base + 2 * (offset + k), + &data, FPGAMAP_SIZE_16); + } + } + + set_control(dev, 0x0049); + + return 0; +} + +int ihs_video_out_set_size(struct udevice *dev, uint x, uint y) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + struct udevice *fpga; + u16 data; + + gdsys_soc_get_fpga(dev, &fpga); + + if (!x || x > 64 || x > MAX_X_CHARS || + !y || y > 32 || y > MAX_Y_CHARS) { + return -EINVAL; + } + + data = ((x - 1) << 8) | (y - 1); + fpgamap_write(fpga, priv->osd_base + REG_XY_SIZE, &data, + FPGAMAP_SIZE_16); + data = 32767 * (priv->res_x - 12 * x) / 65535; + fpgamap_write(fpga, priv->osd_base + REG_X_POS, &data, + FPGAMAP_SIZE_16); + data = 32767 * (priv->res_y - 18 * y) / 65535; + fpgamap_write(fpga, priv->osd_base + REG_Y_POS, &data, + FPGAMAP_SIZE_16); + + return 0; +} + +int ihs_video_out_print(struct udevice *dev, uint x, uint y, ulong color, + char *text) +{ + int res; + u8 buffer[MAX_VIDEOMEM_WIDTH]; + uint k; + uint charcount = strlen(text); + uint len = min(charcount, (uint)(MAX_VIDEOMEM_WIDTH)); + + for (k = 0; k < len; ++k) { + buffer[2 * k] = text[k]; + buffer[2 * k + 1] = color; + } + + res = ihs_video_out_set_mem(dev, x, y, buffer, 2 * len, 1); + + if (res < 0) + return res; + + return 0; +} + +static const struct video_osd_ops ihs_video_out_ops = { + .get_data = ihs_video_out_get_data, + .set_mem = ihs_video_out_set_mem, + .set_size = ihs_video_out_set_size, + .print = ihs_video_out_print, +}; + +int ihs_video_out_probe(struct udevice *dev) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + int len = 0; + struct ofnode_phandle_args phandle_args; + char *mode; + + priv->addr = dev_read_u32_default(dev, "reg", -1); + + priv->osd_base = dev_read_u32_default(dev, "osd_base", -1); + + priv->osd_buffer_base = dev_read_u32_default(dev, "osd_buffer_base", + -1); + + mode = (char *)dev_read_prop(dev, "mode", &len); + + if (!mode) { + printf("%s: Could not read mode property.\n", dev->name); + return -EINVAL; + } + + if (!strcmp(mode, "1024_768_60")) { + priv->sync_src = 2; + priv->res_x = 1024; + priv->res_y = 768; + } else if (!strcmp(mode, "720_400_70")) { + priv->sync_src = 1; + priv->res_x = 720; + priv->res_y = 400; + } else { + priv->sync_src = 0; + priv->res_x = 640; + priv->res_y = 480; + } + + print_info(dev); + + if (dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0, + &phandle_args)) { + printf("%s: Could not get clk_gen node.\n", dev->name); + return -EINVAL; + } + + if (uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node, + &priv->clk_gen)) { + printf("%s: Could not get clk_gen dev.\n", dev->name); + return -EINVAL; + } + + if (dev_read_phandle_with_args(dev, "dp_tx", NULL, 0, 0, + &phandle_args)) { + printf("%s: Could not get dp_tx.\n", dev->name); + return -EINVAL; + } + + if (uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node, + &priv->dp_tx)) { + printf("%s: Could not get dp_tx dev.\n", dev->name); + return -EINVAL; + } + + display_power_on(priv->dp_tx, mode); + + return 0; +} + +U_BOOT_DRIVER(ihs_video_out_drv) = { + .name = "ihs_video_out_drv", + .id = UCLASS_VIDEO_OSD, + .ops = &ihs_video_out_ops, + .of_match = ihs_video_out_ids, + .probe = ihs_video_out_probe, + .priv_auto_alloc_size = sizeof(struct ihs_video_out_priv), +}; -- 2.16.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot