From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mario Six Date: Wed, 23 May 2018 14:09:51 +0200 Subject: [U-Boot] [PATCH v2 2/5] video_osd: Add ihs_video_out driver In-Reply-To: <20180523120954.28182-1-mario.six@gdsys.cc> References: <20180523120954.28182-1-mario.six@gdsys.cc> Message-ID: <20180523120954.28182-2-mario.six@gdsys.cc> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Add a driver for IHS OSDs on IHS FPGAs. Signed-off-by: Mario Six --- v1 -> v2: * Renamed x and y parameters to col and row * Removed duplicate IHS_VIDEO_OUT definition * Switched error return code of ihs_video_out_set_mem to E2BIG * Dropped blank lines between dev_read_u32_default calls * Documented members of ihs_video_out_priv * Turned printfs into debugs * Don't display OSD until something has been written to it * Made the code a bit more readable * Added binding file * Expanded documentation and help * Switched to display_enable * Switched to regmap usage (instead of fpgamap uclass) * Renamed 'dp_tx' property to 'video_tx' --- .../video/osd/gdsys,ihs_video_out.txt | 22 ++ drivers/video/Kconfig | 9 + drivers/video/Makefile | 1 + drivers/video/ihs_video_out.c | 277 +++++++++++++++++++++ 4 files changed, 309 insertions(+) create mode 100644 doc/device-tree-bindings/video/osd/gdsys,ihs_video_out.txt create mode 100644 drivers/video/ihs_video_out.c diff --git a/doc/device-tree-bindings/video/osd/gdsys,ihs_video_out.txt b/doc/device-tree-bindings/video/osd/gdsys,ihs_video_out.txt new file mode 100644 index 00000000000..464374613b8 --- /dev/null +++ b/doc/device-tree-bindings/video/osd/gdsys,ihs_video_out.txt @@ -0,0 +1,22 @@ +* Guntermann & Drunck Integrated Hardware Systems OSD + +Required properties: +- compatible: "gdsys,ihs_video_out" +- reg: Register base for the video registers +- osd_base: Register base for the OSD registers +- osd_buffer_base: Address of the OSD video memory +- mode: The initial resolution and frequency: "1024_768_60", "720_400_70", or + "640_480_70" +- clk_gen: phandle to the pixel clock generator +- dp_tx: phandle to the display associated with the OSD + +Example: + +fpga0_video0 { + compatible = "gdsys,ihs_video_out"; + reg = <0x100 0x40>; + osd_base = <0x180>; + osd_buffer_base = <0x1000>; + dp_tx = <&fpga0_dp_video0>; + clk_gen = <&fpga0_video0_clkgen>; +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 37be23d2fcc..4ca15174bb2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -666,4 +666,13 @@ 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 + Enable support for the gdsys Integrated Hardware Systems (IHS) video + out On-screen Display (OSD) used on gdsys FPGAs to control dynamic + textual overlays of the display outputs. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 1889c5a5208..44252b5989a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -56,6 +56,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 00000000000..3a870e22c2e --- /dev/null +++ b/drivers/video/ihs_video_out.c @@ -0,0 +1,277 @@ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach at gdsys.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_X_CHARS 53 +#define MAX_Y_CHARS 26 +#define MAX_VIDEOMEM_WIDTH (2 * 64) +#define MAX_VIDEOMEM_HEIGHT (2 * 32) +#define CHAR_WIDTH 12 +#define CHAR_HEIGHT 18 + +struct ihs_video_out_regs { + u16 versions; + u16 features; + u16 control; + u16 xy_size; + u16 xy_scale; + u16 x_pos; + u16 y_pos; +}; + +#define ihs_video_out_set(map, member, val) \ + regmap_range_set(map, 1, struct ihs_video_out_regs, member, val) + +#define ihs_video_out_get(map, member, valp) \ + regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp) + +enum { + CONTROL_FILTER_BLACK = (0 << 0), + CONTROL_FILTER_ORIGINAL = (1 << 0), + CONTROL_FILTER_DARKER = (2 << 0), + CONTROL_FILTER_GRAY = (3 << 0), + + CONTROL_MODE_PASSTHROUGH = (0 << 3), + CONTROL_MODE_OSD = (1 << 3), + CONTROL_MODE_AUTO = (2 << 3), + CONTROL_MODE_OFF = (3 << 3), + + CONTROL_ENABLE_OFF = (0 << 6), + CONTROL_ENABLE_ON = (1 << 6), +}; + +struct ihs_video_out_priv { + /* Register map for OSD device */ + struct regmap *map; + /* Pointer to video memory */ + u16 *vidmem; + /* Display width in text columns */ + uint base_width; + /* Display height in text rows */ + uint base_height; + /* x-resolution of the display in pixels */ + uint res_x; + /* y-resolution of the display in pixels */ + uint res_y; + /* OSD's sync mode (resolution + frequency) */ + int sync_src; + /* The display port output for this OSD */ + struct udevice *video_tx; + /* The pixel clock generator for the display */ + struct udevice *clk_gen; +}; + +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); + + if (priv->sync_src) + value |= ((priv->sync_src & 0x7) << 8); + + ihs_video_out_set(priv->map, control, value); + + return 0; +} + +int ihs_video_out_get_info(struct udevice *dev, struct video_osd_info *info) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + u16 versions; + + ihs_video_out_get(priv->map, versions, &versions); + + info->width = priv->base_width; + info->height = priv->base_height; + info->major_version = versions / 100; + info->minor_version = versions % 100; + + return 0; +} + +int ihs_video_out_set_mem(struct udevice *dev, uint col, uint row, u8 *buf, + size_t buflen, uint count) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + uint offset; + uint k, l; + u16 data; + + for (l = 0; l < count; ++l) { + offset = row * priv->base_width + col + l * (buflen / 2); + + for (k = 0; k < buflen / 2; ++k) { + if (offset + k >= priv->base_width * priv->base_height) + return -E2BIG; + + data = buf[2 * k + 1] + 256 * buf[2 * k]; + out_le16(priv->vidmem + offset + k, data); + } + } + + set_control(dev, CONTROL_FILTER_ORIGINAL | + CONTROL_MODE_OSD | + CONTROL_ENABLE_ON); + + return 0; +} + +inline u16 div2_u16(u16 val) +{ + return (32767 * val) / 65535; +} + +int ihs_video_out_set_size(struct udevice *dev, uint col, uint row) +{ + struct ihs_video_out_priv *priv = dev_get_priv(dev); + + if (!col || col > 64 || col > MAX_X_CHARS || + !row || row > 32 || row > MAX_Y_CHARS) { + return -EINVAL; + } + + ihs_video_out_set(priv->map, xy_size, ((col - 1) << 8) | (row - 1)); + /* Center OSD on screen */ + ihs_video_out_set(priv->map, x_pos, + div2_u16(priv->res_x - CHAR_WIDTH * col)); + ihs_video_out_set(priv->map, y_pos, + div2_u16(priv->res_y - CHAR_HEIGHT * row)); + + return 0; +} + +int ihs_video_out_print(struct udevice *dev, uint col, uint row, 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, col, row, buffer, 2 * len, 1); + + if (res < 0) + return res; + + return 0; +} + +static const struct video_osd_ops ihs_video_out_ops = { + .get_info = ihs_video_out_get_info, + .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; + u16 features; + struct display_timing timing; + + regmap_init_mem(dev_ofnode(dev), &priv->map); + + /* Range with index 2 is video memory */ + priv->vidmem = regmap_get_range(priv->map, 2); + + mode = (char *)dev_read_prop(dev, "mode", &len); + + if (!mode) { + debug("%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; + timing.hactive.typ = 1024; + timing.vactive.typ = 768; + } else if (!strcmp(mode, "720_400_70")) { + priv->sync_src = 1; + priv->res_x = 720; + priv->res_y = 400; + timing.hactive.typ = 720; + timing.vactive.typ = 400; + } else { + priv->sync_src = 0; + priv->res_x = 640; + priv->res_y = 480; + timing.hactive.typ = 640; + timing.vactive.typ = 480; + } + + ihs_video_out_get(priv->map, features, &features); + + set_control(dev, CONTROL_FILTER_ORIGINAL | + CONTROL_MODE_OSD | + CONTROL_ENABLE_OFF); + + priv->base_width = ((features & 0x3f00) >> 8) + 1; + priv->base_height = (features & 0x001f) + 1; + + if (dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0, + &phandle_args)) { + debug("%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)) { + debug("%s: Could not get clk_gen dev.\n", dev->name); + return -EINVAL; + } + + if (dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0, + &phandle_args)) { + debug("%s: Could not get video_tx.\n", dev->name); + return -EINVAL; + } + + if (uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node, + &priv->video_tx)) { + debug("%s: Could not get video_tx dev.\n", dev->name); + return -EINVAL; + } + + display_enable(priv->video_tx, 8, &timing); + + 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.11.0