From 9cdbc906721454b2db57dc2a38d236c35900960d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 19 Sep 2018 20:16:32 +0200 Subject: MAINTAINERS: assign include/video*.h The include/video*.h files are part of the drivers in drivers/video. So it makes sense to assign them to the same maintainer. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ea21d59f1e8..3c3f8722431 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -636,6 +636,7 @@ T: git git://git.denx.de/u-boot-video.git F: drivers/video/ F: common/lcd*.c F: include/lcd*.h +F: include/video*.h X86 M: Simon Glass -- cgit v1.2.3 From 5cb4860f3df83da9610baf0ac3a4f2836fdfa328 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 19 Sep 2018 20:18:38 +0200 Subject: dm: video: adjust struct vidconsole_priv description The third component of struct vidconsole_priv is ycur. Signed-off-by: Heinrich Schuchardt --- include/video_console.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/video_console.h b/include/video_console.h index 63af741778d..44e7bdbe290 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -45,7 +45,7 @@ enum color_idx { * * @sdev: stdio device, acting as an output sink * @xcur_frac: Current X position, in fractional units (VID_TO_POS(x)) - * @curr_row: Current Y position in pixels (0=top) + * @ycur: Current Y position in pixels (0=top) * @rows: Number of text rows * @cols: Number of text columns * @x_charsize: Character width in pixels -- cgit v1.2.3 From 662f381aad11246109b660a36b9028fd76714be0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 19 Sep 2018 21:31:48 +0200 Subject: dm: video: support more escape sequences The EFI subsystems needs to know the size of the terminal. If the environment variable stdout = serial,vidconsole this size cannot be read from the video console. So the EFI subsystem sends escape sequences to read the size. With this patch we get support for the following escape sequences: ESC "7" Save cursor position ESC "8" Restore cursor position Signed-off-by: Heinrich Schuchardt --- drivers/video/vidconsole-uclass.c | 75 ++++++++++++++++++++++++++++++++------- include/video_console.h | 28 ++++++++------- 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 0c36a5de0ad..7f95e9c6e56 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -165,6 +165,43 @@ static char *parsenum(char *s, int *num) return end; } +/** + * set_cursor_position() - set cursor position + * + * @priv: private data of the video console + * @row: new row + * @col: new column + */ +static void set_cursor_position(struct vidconsole_priv *priv, int row, int col) +{ + /* + * Ensure we stay in the bounds of the screen. + */ + if (row >= priv->rows) + row = priv->rows - 1; + if (col >= priv->cols) + col = priv->cols - 1; + + priv->ycur = row * priv->y_charsize; + priv->xcur_frac = priv->xstart_frac + + VID_TO_POS(col * priv->x_charsize); +} + +/** + * get_cursor_position() - get cursor position + * + * @priv: private data of the video console + * @row: row + * @col: column + */ +static void get_cursor_position(struct vidconsole_priv *priv, + int *row, int *col) +{ + *row = priv->ycur / priv->y_charsize; + *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) / + priv->x_charsize; +} + /* * Process a character while accumulating an escape string. Chars are * accumulated into escape_buf until the end of escape sequence is @@ -180,8 +217,30 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) /* Sanity checking for bogus ESC sequences: */ if (priv->escape_len >= sizeof(priv->escape_buf)) goto error; - if (priv->escape_len == 0 && ch != '[') - goto error; + if (priv->escape_len == 0) { + switch (ch) { + case '7': + /* Save cursor position */ + get_cursor_position(priv, &priv->row_saved, + &priv->col_saved); + priv->escape = 0; + + return; + case '8': { + /* Restore cursor position */ + int row = priv->row_saved; + int col = priv->col_saved; + + set_cursor_position(priv, row, col); + priv->escape = 0; + return; + } + case '[': + break; + default: + goto error; + } + } priv->escape_buf[priv->escape_len++] = ch; @@ -213,17 +272,7 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) s++; /* ; */ s = parsenum(s, &col); - /* - * Ensure we stay in the bounds of the screen. - */ - if (row >= priv->rows) - row = priv->rows - 1; - if (col >= priv->cols) - col = priv->cols - 1; - - priv->ycur = row * priv->y_charsize; - priv->xcur_frac = priv->xstart_frac + - VID_TO_POS(col * priv->x_charsize); + set_cursor_position(priv, row, col); break; } diff --git a/include/video_console.h b/include/video_console.h index 44e7bdbe290..52a41ac2007 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -43,20 +43,22 @@ enum color_idx { * Drivers must set up @rows, @cols, @x_charsize, @y_charsize in their probe() * method. Drivers may set up @xstart_frac if desired. * - * @sdev: stdio device, acting as an output sink - * @xcur_frac: Current X position, in fractional units (VID_TO_POS(x)) - * @ycur: Current Y position in pixels (0=top) - * @rows: Number of text rows - * @cols: Number of text columns - * @x_charsize: Character width in pixels - * @y_charsize: Character height in pixels + * @sdev: stdio device, acting as an output sink + * @xcur_frac: Current X position, in fractional units (VID_TO_POS(x)) + * @ycur: Current Y position in pixels (0=top) + * @rows: Number of text rows + * @cols: Number of text columns + * @x_charsize: Character width in pixels + * @y_charsize: Character height in pixels * @tab_width_frac: Tab width in fractional units - * @xsize_frac: Width of the display in fractional units + * @xsize_frac: Width of the display in fractional units * @xstart_frac: Left margin for the text console in fractional units - * @last_ch: Last character written to the text console on this line - * @escape: TRUE if currently accumulating an ANSI escape sequence - * @escape_len: Length of accumulated escape sequence so far - * @escape_buf: Buffer to accumulate escape sequence + * @last_ch: Last character written to the text console on this line + * @escape: TRUE if currently accumulating an ANSI escape sequence + * @escape_len: Length of accumulated escape sequence so far + * @col_saved: Saved X position, in fractional units (VID_TO_POS(x)) + * @row_saved: Saved Y position in pixels (0=top) + * @escape_buf: Buffer to accumulate escape sequence */ struct vidconsole_priv { struct stdio_dev sdev; @@ -77,6 +79,8 @@ struct vidconsole_priv { */ int escape; int escape_len; + int row_saved; + int col_saved; char escape_buf[32]; }; -- cgit v1.2.3 From 39a336f116e97d936268b1d317f22a007a01528d Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:29 +0200 Subject: drivers: Add OSD uclass Some devices offer a text-based OSD (on-screen display) that can be programmatically controlled (i.e. text displayed on). Add a uclass to support such devices. Reviewed-by: Simon Glass Signed-off-by: Mario Six --- drivers/video/Kconfig | 8 ++ drivers/video/Makefile | 1 + drivers/video/video_osd-uclass.c | 45 +++++++++ include/dm/uclass-id.h | 1 + include/video_osd.h | 192 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+) create mode 100644 drivers/video/video_osd-uclass.c create mode 100644 include/video_osd.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ed0b21f2a72..4e627a315b9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -683,4 +683,12 @@ config VIDEO_DT_SIMPLEFB The video output is initialized by U-Boot, and kept by the kernel. +config OSD + bool "Enable OSD support" + depends on DM + default n + help + This supports drivers that provide a OSD (on-screen display), which + is a (usually text-oriented) graphics buffer to show information on + a display. endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0f41a23193a..9cec564373e 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o obj-$(CONFIG_LD9040) += ld9040.o obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o +obj-$(CONFIG_OSD) += video_osd-uclass.o obj-$(CONFIG_PXA_LCD) += pxa_lcd.o obj-$(CONFIG_S6E8AX0) += s6e8ax0.o obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o diff --git a/drivers/video/video_osd-uclass.c b/drivers/video/video_osd-uclass.c new file mode 100644 index 00000000000..82136a292bf --- /dev/null +++ b/drivers/video/video_osd-uclass.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include +#include +#include + +int video_osd_get_info(struct udevice *dev, struct video_osd_info *info) +{ + struct video_osd_ops *ops = video_osd_get_ops(dev); + + return ops->get_info(dev, info); +} + +int video_osd_set_mem(struct udevice *dev, uint col, uint row, u8 *buf, + size_t buflen, uint count) +{ + struct video_osd_ops *ops = video_osd_get_ops(dev); + + return ops->set_mem(dev, col, row, buf, buflen, count); +} + +int video_osd_set_size(struct udevice *dev, uint col, uint row) +{ + struct video_osd_ops *ops = video_osd_get_ops(dev); + + return ops->set_size(dev, col, row); +} + +int video_osd_print(struct udevice *dev, uint col, uint row, ulong color, + char *text) +{ + struct video_osd_ops *ops = video_osd_get_ops(dev); + + return ops->print(dev, col, row, color, text); +} + +UCLASS_DRIVER(video_osd) = { + .id = UCLASS_VIDEO_OSD, + .name = "video_osd", + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 7027ea076db..15abdce5e52 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -92,6 +92,7 @@ enum uclass_id { UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ + UCLASS_VIDEO_OSD, /* On-screen display */ UCLASS_WDT, /* Watchdot Timer driver */ UCLASS_COUNT, diff --git a/include/video_osd.h b/include/video_osd.h new file mode 100644 index 00000000000..01ac94b425c --- /dev/null +++ b/include/video_osd.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#ifndef _VIDEO_OSD_H_ +#define _VIDEO_OSD_H_ + +struct video_osd_info { + /* The width of the OSD display in columns */ + uint width; + /* The height of the OSD display in rows */ + uint height; + /* The major version of the OSD device */ + uint major_version; + /* The minor version of the OSD device */ + uint minor_version; +}; + +/** + * struct video_osd_ops - driver operations for OSD uclass + * + * The OSD uclass implements support for text-oriented on-screen displays, + * which are taken to be devices that independently display a graphical + * text-based overlay over the video output of an associated display. + * + * The functions defined by the uclass support writing text to the display in + * either a generic form (by specifying a string, a driver-specific color value + * for the text, and screen coordinates in rows and columns) or a + * driver-specific form (by specifying "raw" driver-specific data to display at + * a given coordinate). + * + * Functions to read device information and set the size of the virtual OSD + * screen (in rows and columns) are also supported. + * + * Drivers should support these operations unless otherwise noted. These + * operations are intended to be used by uclass code, not directly from + * other code. + */ +struct video_osd_ops { + /** + * get_info() - Get information about a OSD instance + * + * A OSD instance may keep some internal data about itself. This + * function can be used to access this data. + * + * @dev: OSD instance to query. + * @info: Pointer to a structure that takes the information read + * from the OSD instance. + * @return 0 if OK, -ve on error. + */ + int (*get_info)(struct udevice *dev, struct video_osd_info *info); + + /** + * set_mem() - Write driver-specific text data to OSD screen + * + * The passed data are device-specific, and it's up to the driver how + * to interpret them. How the count parameter is interpreted is also + * driver-specific; most likely the given data will be written to the + * OSD count times back-to-back, which is e.g. convenient for filling + * areas of the OSD with a single character. + * + * For example a invocation of + * + * video_osd_set_mem(dev, 0, 0, "A", 1, 10); + * + * will write the device-specific text data "A" to the positions (0, 0) + * to (9, 0) on the OSD. + * + * Device-specific text data may, e.g. be a special encoding of glyphs + * to display and color values in binary format. + * + * @dev: OSD instance to write to. + * @col: Horizontal character coordinate to write to. + * @row Vertical character coordinate to write to. + * @buf: Array containing device-specific data to write to the + * specified coordinate on the OSD screen. + * @buflen: Length of the data in the passed buffer (in byte). + * @count: Write count many repetitions of the given text data + * @return 0 if OK, -ve on error. + */ + int (*set_mem)(struct udevice *dev, uint col, uint row, u8 *buf, + size_t buflen, uint count); + + /** + * set_size() - Set the position and dimension of the OSD's + * writeable window + * + * @dev: OSD instance to write to. + * @col The number of characters in the window's columns + * @row The number of characters in the window's rows + * @return 0 if OK, -ve on error. + */ + int (*set_size)(struct udevice *dev, uint col, uint row); + + /** + * print() - Print a string in a given color to specified coordinates + * on the OSD + * + * @dev: OSD instance to write to. + * @col The x-coordinate of the position the string should be + * written to + * @row The y-coordinate of the position the string should be + * written to + * @color: The color in which the specified string should be + * printed; the interpretation of the value is + * driver-specific, and possible values should be defined + * e.g. in a driver include file. + * @text: The string data that should be printed on the OSD + * @return 0 if OK, -ve on error. + */ + int (*print)(struct udevice *dev, uint col, uint row, ulong color, + char *text); +}; + +#define video_osd_get_ops(dev) ((struct video_osd_ops *)(dev)->driver->ops) + +/** + * video_osd_get_info() - Get information about a OSD instance + * + * A OSD instance may keep some internal data about itself. This function can + * be used to access this data. + * + * @dev: OSD instance to query. + * @info: Pointer to a structure that takes the information read from the + * OSD instance. + * @return 0 if OK, -ve on error. + */ +int video_osd_get_info(struct udevice *dev, struct video_osd_info *info); + +/** + * video_osd_set_mem() - Write text data to OSD memory + * + * The passed data are device-specific, and it's up to the driver how to + * interpret them. How the count parameter is interpreted is also + * driver-specific; most likely the given data will be written to the OSD count + * times back-to-back, which is e.g. convenient for filling areas of the OSD + * with a single character. + * + * For example a invocation of + * + * video_osd_set_mem(dev, 0, 0, "A", 1, 10); + * + * will write the device-specific text data "A" to the positions (0, 0) to (9, + * 0) on the OSD. + * + * Device-specific text data may, e.g. be a special encoding of glyphs to + * display and color values in binary format. + * + * @dev: OSD instance to write to. + * @col: Horizontal character coordinate to write to. + * @row Vertical character coordinate to write to. + * @buf: Array containing device-specific data to write to the specified + * coordinate on the OSD screen. + * @buflen: Length of the data in the passed buffer (in byte). + * @count: Write count many repetitions of the given text data + * @return 0 if OK, -ve on error. + */ +int video_osd_set_mem(struct udevice *dev, uint col, uint row, u8 *buf, + size_t buflen, uint count); + +/** + * video_osd_set_size() - Set the position and dimension of the OSD's + * writeable window + * + * @dev: OSD instance to write to. + * @col The number of characters in the window's columns + * @row The number of characters in the window's rows + * @return 0 if OK, -ve on error. + */ +int video_osd_set_size(struct udevice *dev, uint col, uint row); + +/** + * video_osd_print() - Print a string in a given color to specified coordinates + * on the OSD + * + * @dev: OSD instance to write to. + * @col The x-coordinate of the position the string should be written + * to + * @row The y-coordinate of the position the string should be written + * to + * @color: The color in which the specified string should be printed; the + * interpretation of the value is driver-specific, and possible + * values should be defined e.g. in a driver include file. + * @text: The string data that should be printed on the OSD + * @return 0 if OK, -ve on error. + */ +int video_osd_print(struct udevice *dev, uint col, uint row, ulong color, + char *text); + +#endif /* !_VIDEO_OSD_H_ */ -- cgit v1.2.3 From 9671f696e52b1af72fe5566bf5a6fefc6a6fefa5 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:30 +0200 Subject: video_osd: Add ihs_video_out driver Add a driver for IHS OSDs on IHS FPGAs. Reviewed-by: Simon Glass Signed-off-by: Mario Six --- .../video/osd/gdsys,ihs_video_out.txt | 23 ++ drivers/video/Kconfig | 9 + drivers/video/Makefile | 1 + drivers/video/ihs_video_out.c | 341 +++++++++++++++++++++ 4 files changed, 374 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..d7aacc827ed --- /dev/null +++ b/doc/device-tree-bindings/video/osd/gdsys,ihs_video_out.txt @@ -0,0 +1,23 @@ +* Guntermann & Drunck Integrated Hardware Systems OSD + +Required properties: +- compatible: "gdsys,ihs_video_out" +- reg: A combination of three register spaces: + - Register base for the video registers + - Register base for the OSD registers + - 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 + 0x180 0x20 + 0x1000 0x1000>; + dp_tx = <&fpga0_dp_video0>; + clk_gen = <&fpga0_video0_clkgen>; +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4e627a315b9..1e2cbe7afcf 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -691,4 +691,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 9cec564373e..290747ba94f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o obj-$(CONFIG_FORMIKE) += formike.o obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o +obj-$(CONFIG_IHS_VIDEO_OUT) += ihs_video_out.o obj-$(CONFIG_LD9040) += ld9040.o obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o diff --git a/drivers/video/ihs_video_out.c b/drivers/video/ihs_video_out.c new file mode 100644 index 00000000000..5cdf17aec14 --- /dev/null +++ b/drivers/video/ihs_video_out.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.de + */ + +#include +#include +#include +#include +#include +#include + +static const uint MAX_X_CHARS = 53; +static const uint MAX_Y_CHARS = 26; +static const uint MAX_VIDEOMEM_WIDTH = 64; +static const uint MAX_VIDEOMEM_HEIGHT = 32; +static const uint CHAR_WIDTH = 12; +static const uint CHAR_HEIGHT = 18; + +static const u16 BASE_WIDTH_MASK = 0x3f00; +static const uint BASE_WIDTH_SHIFT = 8; +static const u16 BASE_HEIGTH_MASK = 0x001f; +static const uint BASE_HEIGTH_SHIFT; + +struct ihs_video_out_regs { + /* Device version register */ + u16 versions; + /* Device feature register */ + u16 features; + /* Device control register */ + u16 control; + /* Register controlling screen size */ + u16 xy_size; + /* Register controlling screen scaling */ + u16 xy_scale; + /* Register controlling screen x position */ + u16 x_pos; + /* Register controlling screen y position */ + 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" }, + { } +}; + +/** + * set_control() - Set the control register to a given value + * + * The current value of sync_src is preserved by the function automatically. + * + * @dev: the OSD device whose control register to set + * @value: the 16-bit value to write to the control register + * Return: 0 + */ +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); + int res; + uint offset; + uint k, rep; + u16 data; + + /* Repetitions (controlled via count parmeter) */ + for (rep = 0; rep < count; ++rep) { + offset = row * priv->base_width + col + rep * (buflen / 2); + + /* Write a single buffer copy */ + for (k = 0; k < buflen / 2; ++k) { + uint max_size = priv->base_width * priv->base_height; + + if (offset + k >= max_size) { + debug("%s: Write would be out of OSD bounds\n", + dev->name); + return -E2BIG; + } + + data = buf[2 * k + 1] + 256 * buf[2 * k]; + out_le16(priv->vidmem + offset + k, data); + } + } + + res = set_control(dev, CONTROL_FILTER_ORIGINAL | + CONTROL_MODE_OSD | + CONTROL_ENABLE_ON); + if (res) { + debug("%s: Could not set control register\n", dev->name); + return res; + } + + return 0; +} + +/** + * div2_u16() - Approximately divide a 16-bit number by 2 + * + * @val: The 16-bit value to divide by two + * Return: The approximate division of val by two + */ +static 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 > MAX_VIDEOMEM_WIDTH || col > MAX_X_CHARS || + !row || row > MAX_VIDEOMEM_HEIGHT || row > MAX_Y_CHARS) { + debug("%s: Desired OSD size invalid\n", dev->name); + 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[2 * MAX_VIDEOMEM_WIDTH]; + uint k; + uint charcount = strlen(text); + uint len = min(charcount, 2 * 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) { + debug("%s: Could not write to video memory\n", dev->name); + 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); + struct ofnode_phandle_args phandle_args; + const char *mode; + u16 features; + struct display_timing timing; + int res; + + res = regmap_init_mem(dev_ofnode(dev), &priv->map); + if (!res) { + debug("%s: Could initialize regmap (err = %d)\n", dev->name, + res); + return res; + } + + /* Range with index 2 is video memory */ + priv->vidmem = regmap_get_range(priv->map, 2); + + mode = dev_read_string(dev, "mode"); + 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); + + res = set_control(dev, CONTROL_FILTER_ORIGINAL | + CONTROL_MODE_OSD | + CONTROL_ENABLE_OFF); + if (res) { + debug("%s: Could not set control register (err = %d)\n", + dev->name, res); + return res; + } + + priv->base_width = ((features & BASE_WIDTH_MASK) + >> BASE_WIDTH_SHIFT) + 1; + priv->base_height = ((features & BASE_HEIGTH_MASK) + >> BASE_HEIGTH_SHIFT) + 1; + + res = dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0, + &phandle_args); + if (res) { + debug("%s: Could not get clk_gen node (err = %d)\n", + dev->name, res); + return -EINVAL; + } + + res = uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node, + &priv->clk_gen); + if (res) { + debug("%s: Could not get clk_gen dev (err = %d)\n", + dev->name, res); + return -EINVAL; + } + + res = dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0, + &phandle_args); + if (res) { + debug("%s: Could not get video_tx (err = %d)\n", + dev->name, res); + return -EINVAL; + } + + res = uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node, + &priv->video_tx); + if (res) { + debug("%s: Could not get video_tx dev (err = %d)\n", + dev->name, res); + return -EINVAL; + } + + res = display_enable(priv->video_tx, 8, &timing); + if (res) { + debug("%s: Could not enable the display (err = %d)\n", + dev->name, res); + return res; + } + + 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), +}; -- cgit v1.2.3 From 4eea531859edc7e23fcffadee47757a0c6e0959f Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:31 +0200 Subject: video_osd: Add osd sandbox driver and tests Add sandbox driver and tests for the new OSD uclass. Signed-off-by: Mario Six Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 5 + arch/sandbox/include/asm/test.h | 8 ++ configs/sandbox64_defconfig | 3 + configs/sandbox_defconfig | 3 + configs/sandbox_flattree_defconfig | 3 + configs/sandbox_noblk_defconfig | 3 + configs/sandbox_spl_defconfig | 3 + drivers/video/Kconfig | 6 ++ drivers/video/Makefile | 1 + drivers/video/sandbox_osd.c | 161 ++++++++++++++++++++++++++++ drivers/video/sandbox_osd.h | 13 +++ test/dm/Makefile | 1 + test/dm/osd.c | 210 +++++++++++++++++++++++++++++++++++++ 13 files changed, 420 insertions(+) create mode 100644 drivers/video/sandbox_osd.c create mode 100644 drivers/video/sandbox_osd.h create mode 100644 test/dm/osd.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index b8524e3b7d6..cfa47bcd0de 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -37,6 +37,7 @@ usb1 = &usb_1; usb2 = &usb_2; axi0 = &axi; + osd0 = "/osd"; }; a-test { @@ -642,6 +643,10 @@ }; }; }; + + osd { + compatible = "sandbox,sandbox_osd"; + }; }; #include "sandbox_pmic.dtsi" diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index c8ae52b248d..89f3d90c734 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -90,4 +90,12 @@ long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time); int sandbox_usb_keyb_add_string(struct udevice *dev, const char *str); +/** + * sandbox_osd_get_mem() - get the internal memory of a sandbox OSD + * + * @dev: OSD device for which to access the internal memory for + * @buf: pointer to buffer to receive the OSD memory data + * @buflen: length of buffer in bytes + */ +int sandbox_osd_get_mem(struct udevice *dev, u8 *buf, size_t buflen); #endif diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 27797c6990b..fb511d411a4 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -39,6 +39,7 @@ CONFIG_CMD_GPT=y CONFIG_CMD_GPT_RENAME=y CONFIG_CMD_IDE=y CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y CONFIG_CMD_READ=y CONFIG_CMD_REMOTEPROC=y @@ -186,6 +187,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y CONFIG_WDT=y CONFIG_WDT_SANDBOX=y CONFIG_FS_CBFS=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 0b209686bf9..af8375e22e5 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -41,6 +41,7 @@ CONFIG_CMD_GPT=y CONFIG_CMD_GPT_RENAME=y CONFIG_CMD_IDE=y CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y CONFIG_CMD_READ=y CONFIG_CMD_REMOTEPROC=y @@ -192,6 +193,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y CONFIG_WDT=y CONFIG_WDT_SANDBOX=y CONFIG_FS_CBFS=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 618d6462a8c..3dcfdcc5391 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -31,6 +31,7 @@ CONFIG_CMD_DEMO=y CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SF=y @@ -167,6 +168,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index a7691daa008..34c46142298 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -35,6 +35,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_IDE=y CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SF=y @@ -166,6 +167,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y CONFIG_CMD_DHRYSTONE=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dad5e1ce770..746f59fae85 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -43,6 +43,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_IDE=y CONFIG_CMD_I2C=y +CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SF=y @@ -185,6 +186,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_OSD=y +CONFIG_SANDBOX_OSD=y CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y CONFIG_CMD_DHRYSTONE=y diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1e2cbe7afcf..c68066692d7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -692,6 +692,12 @@ config OSD is a (usually text-oriented) graphics buffer to show information on a display. +config SANDBOX_OSD + bool "Enable sandbox OSD" + depends on OSD + help + Enable support for sandbox OSD device used for testing purposes. + config IHS_VIDEO_OUT bool "Enable IHS video out driver" depends on OSD diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 290747ba94f..b31017e436d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o obj-$(CONFIG_OSD) += video_osd-uclass.o obj-$(CONFIG_PXA_LCD) += pxa_lcd.o +obj-$(CONFIG_SANDBOX_OSD) += sandbox_osd.o obj-$(CONFIG_S6E8AX0) += s6e8ax0.o obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o diff --git a/drivers/video/sandbox_osd.c b/drivers/video/sandbox_osd.c new file mode 100644 index 00000000000..dd84489add0 --- /dev/null +++ b/drivers/video/sandbox_osd.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ +#include +#include +#include +#include + +#include "sandbox_osd.h" + +struct sandbox_osd_priv { + uint width; + uint height; + u16 *buf; +}; + +static const struct udevice_id sandbox_osd_ids[] = { + { .compatible = "sandbox,sandbox_osd" }, + { } +}; + +inline u16 make_memval(u8 chr, u8 color) +{ + return chr * 0x100 + color; +} + +int sandbox_osd_get_info(struct udevice *dev, struct video_osd_info *info) +{ + struct sandbox_osd_priv *priv = dev_get_priv(dev); + + info->width = priv->width; + info->height = priv->height; + info->major_version = 1; + info->minor_version = 0; + + return 0; +} + +int sandbox_osd_set_mem(struct udevice *dev, uint col, uint row, u8 *buf, + size_t buflen, uint count) +{ + struct sandbox_osd_priv *priv = dev_get_priv(dev); + int pos; + u8 *mem = (u8 *)priv->buf; + int i; + + pos = 2 * (row * priv->width + col); + + if (pos >= 2 * (priv->width * priv->height)) + return -EINVAL; + + for (i = 0; i < count; i++) + memcpy(mem + pos + (i * buflen), buf, buflen); + + return 0; +} + +int _sandbox_osd_set_size(struct udevice *dev, uint col, uint row) +{ + struct sandbox_osd_priv *priv = dev_get_priv(dev); + int i; + uint size; + + priv->width = col; + priv->height = row; + size = priv->width * priv->height; + if (!priv->buf) + priv->buf = calloc(size, sizeof(u16)); + else + priv->buf = realloc(priv->buf, size * sizeof(u16)); + + if (!priv->buf) + return -ENOMEM; + + /* Fill OSD with black spaces */ + for (i = 0; i < size; i++) + priv->buf[i] = make_memval(' ', 'k'); + + return 0; +} + +int sandbox_osd_set_size(struct udevice *dev, uint col, uint row) +{ + return _sandbox_osd_set_size(dev, col, row); +} + +int sandbox_osd_print(struct udevice *dev, uint col, uint row, ulong color, + char *text) +{ + struct sandbox_osd_priv *priv = dev_get_priv(dev); + char cval; + char *p; + int pos; + + if (col >= priv->width || row >= priv->height) + return -EINVAL; + + switch (color) { + case COLOR_BLACK: + cval = 'k'; + break; + case COLOR_WHITE: + cval = 'w'; + break; + case COLOR_RED: + cval = 'r'; + break; + case COLOR_GREEN: + cval = 'g'; + break; + case COLOR_BLUE: + cval = 'b'; + break; + default: + return -EINVAL; + } + + p = text; + pos = row * priv->width + col; + + while (*p) + priv->buf[pos++] = make_memval(*(p++), cval); + + return 0; +} + +int sandbox_osd_get_mem(struct udevice *dev, u8 *buf, size_t buflen) +{ + struct sandbox_osd_priv *priv = dev_get_priv(dev); + uint memsize = 2 * (priv->width * priv->height); + + if (buflen < memsize) + return -EINVAL; + + memcpy(buf, priv->buf, memsize); + + return 0; +} + +static const struct video_osd_ops sandbox_osd_ops = { + .get_info = sandbox_osd_get_info, + .set_mem = sandbox_osd_set_mem, + .set_size = sandbox_osd_set_size, + .print = sandbox_osd_print, +}; + +int sandbox_osd_probe(struct udevice *dev) +{ + return _sandbox_osd_set_size(dev, 10, 10); +} + +U_BOOT_DRIVER(sandbox_osd_drv) = { + .name = "sandbox_osd_drv", + .id = UCLASS_VIDEO_OSD, + .ops = &sandbox_osd_ops, + .of_match = sandbox_osd_ids, + .probe = sandbox_osd_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_osd_priv), +}; diff --git a/drivers/video/sandbox_osd.h b/drivers/video/sandbox_osd.h new file mode 100644 index 00000000000..15a2c91c52d --- /dev/null +++ b/drivers/video/sandbox_osd.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +enum { + COLOR_BLACK, + COLOR_WHITE, + COLOR_RED, + COLOR_GREEN, + COLOR_BLUE, +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 8b1ba915d01..16033271f8d 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o obj-y += ofnode.o +obj-$(CONFIG_OSD) += osd.o obj-$(CONFIG_DM_PCI) += pci.o obj-$(CONFIG_PHY) += phy.o obj-$(CONFIG_POWER_DOMAIN) += power-domain.o diff --git a/test/dm/osd.c b/test/dm/osd.c new file mode 100644 index 00000000000..6910690b3a9 --- /dev/null +++ b/test/dm/osd.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../../drivers/video/sandbox_osd.h" + +const uint memsize = 2 * 10 * 10; + +static void split(u8 *mem, uint size, u8 *text, u8 *colors) +{ + int i; + u16 *p = (u16 *)mem; + + for (i = 0; i < size; i++) { + colors[i] = p[i] % 0x100; + text[i] = p[i] / 0x100; + } +} + +static void print_mem(u8 *mem, uint width, uint height) +{ + const uint memsize = 2 * 10 * 10; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + int i; + + split(mem, memsize / 2, text, colors); + + for (i = 0; i < width * height; i++) { + printf("%c", text[i]); + if (i > 0 && ((i + 1) % width) == 0) + printf("\n"); + } + + printf("\n"); + + for (i = 0; i < width * height; i++) { + printf("%c", colors[i]); + if (i > 0 && ((i + 1) % width) == 0) + printf("\n"); + } +} + +static int dm_test_osd_basics(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 mem[memsize + 1]; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + struct video_osd_info info; + + ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev)); + + video_osd_get_info(dev, &info); + + ut_asserteq(10, info.width); + ut_asserteq(10, info.height); + ut_asserteq(1, info.major_version); + ut_asserteq(0, info.minor_version); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_assertok(memcmp(text, " " + " " + " " + " " + " " + " " + " " + " " + " " + " ", memsize / 2)); + + ut_assertok(memcmp(colors, "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk", memsize / 2)); + + print_mem(mem, 10, 10); + + ut_assertok(video_osd_print(dev, 1, 1, COLOR_RED, "Blah")); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_assertok(memcmp(text, " " + " Blah " + " " + " " + " " + " " + " " + " " + " " + " ", memsize / 2)); + + ut_assertok(memcmp(colors, "kkkkkkkkkk" + "krrrrkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk", memsize / 2)); + + print_mem(mem, 10, 10); + + return 0; +} + +DM_TEST(dm_test_osd_basics, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_osd_extended(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 mem[memsize + 1]; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + struct video_osd_info info; + u16 val; + + ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev)); + + ut_assertok(video_osd_set_size(dev, 20, 5)); + + video_osd_get_info(dev, &info); + + ut_asserteq(20, info.width); + ut_asserteq(5, info.height); + ut_asserteq(1, info.major_version); + ut_asserteq(0, info.minor_version); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_assertok(memcmp(text, " " + " " + " " + " " + " ", memsize / 2)); + + ut_assertok(memcmp(colors, "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk", memsize / 2)); + + print_mem(mem, 20, 5); + + /* Draw green border */ + val = '-' * 0x100 + 'g'; + ut_assertok(video_osd_set_mem(dev, 1, 0, (u8 *)&val, 2, 18)); + ut_assertok(video_osd_set_mem(dev, 1, 4, (u8 *)&val, 2, 18)); + ut_assertok(video_osd_print(dev, 0, 1, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 2, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 3, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 1, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 2, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 3, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 0, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 19, 0, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 19, 4, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 0, 4, COLOR_GREEN, "+")); + + /* Add menu caption and entries */ + ut_assertok(video_osd_print(dev, 5, 0, COLOR_GREEN, " OSD menu ")); + ut_assertok(video_osd_print(dev, 2, 1, COLOR_BLUE, " * Entry 1")); + ut_assertok(video_osd_print(dev, 2, 2, COLOR_BLUE, "(*) Entry 2")); + ut_assertok(video_osd_print(dev, 2, 3, COLOR_BLUE, " * Entry 3")); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + print_mem(mem, 20, 5); + + ut_assertok(memcmp(text, "+---- OSD menu ----+" + "| * Entry 1 |" + "| (*) Entry 2 |" + "| * Entry 3 |" + "+------------------+", memsize / 2)); + + ut_assertok(memcmp(colors, "gggggggggggggggggggg" + "gkbbbbbbbbbbbkkkkkkg" + "gkbbbbbbbbbbbkkkkkkg" + "gkbbbbbbbbbbbkkkkkkg" + "gggggggggggggggggggg", memsize / 2)); + + return 0; +} + +DM_TEST(dm_test_osd_extended, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.2.3 From 41f67e3bf343e7d8408956574f83ef7c13d2d0b4 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:32 +0200 Subject: test: Add ut_asserteq_mem Add a unit test assert-method, which compares two given memory areas for byte-wise equality. Signed-off-by: Mario Six Reviewed-by: Simon Glass --- include/test/ut.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/test/ut.h b/include/test/ut.h index fce75fd12a0..19bcb8c3748 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -78,6 +78,24 @@ void ut_failf(struct unit_test_state *uts, const char *fname, int line, } \ } +/* Assert that two memory areas are equal */ +#define ut_asserteq_mem(expr1, expr2, len) { \ + const u8 *val1 = (u8 *)(expr1), *val2 = (u8 *)(expr2); \ + const uint __len = len; \ + \ + if (memcmp(val1, val2, __len)) { \ + char __buf1[64 + 1] = "\0"; \ + char __buf2[64 + 1] = "\0"; \ + bin2hex(__buf1, val1, min(__len, (uint)32)); \ + bin2hex(__buf2, val2, min(__len, (uint)32)); \ + ut_failf(uts, __FILE__, __LINE__, __func__, \ + #expr1 " = " #expr2, \ + "Expected \"%s\", got \"%s\"", \ + __buf1, __buf2); \ + return CMD_RET_FAILURE; \ + } \ +} + /* Assert that two pointers are equal */ #define ut_asserteq_ptr(expr1, expr2) { \ const void *val1 = (expr1), *val2 = (expr2); \ -- cgit v1.2.3 From 87be2fe6ec1a689362a03a8e04a6abf6029ee2d1 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:33 +0200 Subject: test: list: Add tests for hexdump.c Add tests for the hex2bin, bin2hex, and hex_to_bin functions, which were recently added to U-Boot. Signed-off-by: Mario Six Reviewed-by: Simon Glass --- test/Makefile | 1 + test/lib/Makefile | 5 +++ test/lib/hexdump.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 test/lib/Makefile create mode 100644 test/lib/hexdump.c diff --git a/test/Makefile b/test/Makefile index a5f52fd5ad6..1e434730b68 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SANDBOX) += print_ut.o obj-$(CONFIG_UT_TIME) += time_ut.o obj-$(CONFIG_UT_UNICODE) += unicode_ut.o obj-$(CONFIG_$(SPL_)LOG) += log/ +obj-$(CONFIG_UNIT_TEST) += lib/ diff --git a/test/lib/Makefile b/test/lib/Makefile new file mode 100644 index 00000000000..ea68fae566f --- /dev/null +++ b/test/lib/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2018 +# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc +obj-y += hexdump.o diff --git a/test/lib/hexdump.c b/test/lib/hexdump.c new file mode 100644 index 00000000000..e8b3e566e0f --- /dev/null +++ b/test/lib/hexdump.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include +#include +#include +#include + +static int lib_test_hex_to_bin(struct unit_test_state *uts) +{ + return 0; + + ut_asserteq(0x0, hex_to_bin('0')); + ut_asserteq(0x1, hex_to_bin('1')); + ut_asserteq(0x2, hex_to_bin('2')); + ut_asserteq(0x3, hex_to_bin('3')); + ut_asserteq(0x4, hex_to_bin('4')); + ut_asserteq(0x5, hex_to_bin('5')); + ut_asserteq(0x6, hex_to_bin('6')); + ut_asserteq(0x7, hex_to_bin('7')); + ut_asserteq(0x8, hex_to_bin('8')); + ut_asserteq(0x9, hex_to_bin('9')); + ut_asserteq(0xa, hex_to_bin('a')); + ut_asserteq(0xb, hex_to_bin('b')); + ut_asserteq(0xc, hex_to_bin('c')); + ut_asserteq(0xd, hex_to_bin('d')); + ut_asserteq(0xe, hex_to_bin('e')); + ut_asserteq(0xf, hex_to_bin('f')); + ut_asserteq(-1, hex_to_bin('g')); +} + +DM_TEST(lib_test_hex_to_bin, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int lib_test_hex2bin(struct unit_test_state *uts) +{ + u8 dst[4]; + + hex2bin(dst, "649421de", 4); + ut_asserteq_mem("\x64\x94\x21\xde", dst, 4); + hex2bin(dst, "aa2e7545", 4); + ut_asserteq_mem("\xaa\x2e\x75\x45", dst, 4); + hex2bin(dst, "75453bc5", 4); + ut_asserteq_mem("\x75\x45\x3b\xc5", dst, 4); + hex2bin(dst, "a16884c3", 4); + ut_asserteq_mem("\xa1\x68\x84\xc3", dst, 4); + hex2bin(dst, "156b2e5e", 4); + ut_asserteq_mem("\x15\x6b\x2e\x5e", dst, 4); + hex2bin(dst, "2e035fff", 4); + ut_asserteq_mem("\x2e\x03\x5f\xff", dst, 4); + hex2bin(dst, "0ffce99f", 4); + ut_asserteq_mem("\x0f\xfc\xe9\x9f", dst, 4); + hex2bin(dst, "d3999443", 4); + ut_asserteq_mem("\xd3\x99\x94\x43", dst, 4); + hex2bin(dst, "91dd87bc", 4); + ut_asserteq_mem("\x91\xdd\x87\xbc", dst, 4); + hex2bin(dst, "7fec8963", 4); + ut_asserteq_mem("\x7f\xec\x89\x63", dst, 4); + + return 0; +} + +DM_TEST(lib_test_hex2bin, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int lib_test_bin2hex(struct unit_test_state *uts) +{ + char dst[8 + 1] = "\0"; + + bin2hex(dst, "\x64\x94\x21\xde", 4); + ut_asserteq_str("649421de", dst); + bin2hex(dst, "\xaa\x2e\x75\x45", 4); + ut_asserteq_str("aa2e7545", dst); + bin2hex(dst, "\x75\x45\x3b\xc5", 4); + ut_asserteq_str("75453bc5", dst); + bin2hex(dst, "\xa1\x68\x84\xc3", 4); + ut_asserteq_str("a16884c3", dst); + bin2hex(dst, "\x15\x6b\x2e\x5e", 4); + ut_asserteq_str("156b2e5e", dst); + bin2hex(dst, "\x2e\x03\x5f\xff", 4); + ut_asserteq_str("2e035fff", dst); + bin2hex(dst, "\x0f\xfc\xe9\x9f", 4); + ut_asserteq_str("0ffce99f", dst); + bin2hex(dst, "\xd3\x99\x94\x43", 4); + ut_asserteq_str("d3999443", dst); + bin2hex(dst, "\x91\xdd\x87\xbc", 4); + ut_asserteq_str("91dd87bc", dst); + bin2hex(dst, "\x7f\xec\x89\x63", 4); + ut_asserteq_str("7fec8963", dst); + + return 0; +} + +DM_TEST(lib_test_bin2hex, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.2.3 From 3bf65cb56cc844473146d977b21c64504f191aff Mon Sep 17 00:00:00 2001 From: Mario Six Date: Thu, 27 Sep 2018 09:19:34 +0200 Subject: cmd: Add osd commands Add command to query information from and write text to on-screen display (OSD) devices. Signed-off-by: Mario Six Reviewed-by: Simon Glass --- board/gdsys/common/Makefile | 4 + board/gdsys/common/osd_cmd.c | 146 ++++++++++++++++++++++ board/gdsys/mpc8308/Kconfig | 11 ++ cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/osd.c | 291 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 461 insertions(+) create mode 100644 board/gdsys/common/osd_cmd.c create mode 100644 cmd/osd.c diff --git a/board/gdsys/common/Makefile b/board/gdsys/common/Makefile index 698ea3b02bc..af9058a5d77 100644 --- a/board/gdsys/common/Makefile +++ b/board/gdsys/common/Makefile @@ -15,3 +15,7 @@ obj-$(CONFIG_STRIDER) += mclink.o dp501.o phy.o ioep-fpga.o adv7611.o ch7301.o obj-$(CONFIG_STRIDER) += fanctrl.o obj-$(CONFIG_STRIDER_CON) += osd.o obj-$(CONFIG_STRIDER_CON_DP) += osd.o + +ifdef CONFIG_OSD +obj-$(CONFIG_GDSYS_LEGACY_OSD_CMDS) += osd_cmd.o +endif diff --git a/board/gdsys/common/osd_cmd.c b/board/gdsys/common/osd_cmd.c new file mode 100644 index 00000000000..53179b98113 --- /dev/null +++ b/board/gdsys/common/osd_cmd.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + */ + +#include +#include +#include +#include +#include + +static int do_osd_write(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *dev; + uint x, y; + uint count; + char *hexstr; + u8 *buffer; + size_t buflen; + int res; + + if (argc < 4 || (strlen(argv[3])) % 2) + return CMD_RET_USAGE; + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + hexstr = argv[3]; + count = (argc > 4) ? simple_strtoul(argv[4], NULL, 16) : 1; + + buflen = strlen(hexstr) / 2; + + buffer = malloc(buflen); + if (!buffer) { + puts("Memory allocation failure\n"); + return CMD_RET_FAILURE; + } + + res = hex2bin(buffer, hexstr, buflen); + if (res) { + free(buffer); + puts("Hexadecimal input contained invalid characters\n"); + return CMD_RET_FAILURE; + } + + for (uclass_first_device(UCLASS_VIDEO_OSD, &dev); + dev; + uclass_next_device(&dev)) { + int res; + + res = video_osd_set_mem(dev, x, y, buffer, buflen, count); + if (res) { + free(buffer); + printf("Could not write to video mem on osd %s\n", + dev->name); + return CMD_RET_FAILURE; + } + } + + free(buffer); + + return CMD_RET_SUCCESS; +} + +static int do_osd_print(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *dev; + uint x, y; + u8 color; + char *text; + + if (argc < 5) + return CMD_RET_USAGE; + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + color = simple_strtoul(argv[3], NULL, 16); + text = argv[4]; + + for (uclass_first_device(UCLASS_VIDEO_OSD, &dev); + dev; + uclass_next_device(&dev)) { + int res; + + res = video_osd_print(dev, x, y, color, text); + if (res) { + printf("Could not print string to osd %s\n", dev->name); + return CMD_RET_FAILURE; + } + } + + return CMD_RET_SUCCESS; +} + +static int do_osd_size(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *dev; + uint x, y; + + if (argc < 3) + return CMD_RET_USAGE; + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + + for (uclass_first_device(UCLASS_VIDEO_OSD, &dev); + dev; + uclass_next_device(&dev)) { + int res; + + res = video_osd_set_size(dev, x, y); + + if (res) { + printf("Could not set size on osd %s\n", dev->name); + return CMD_RET_FAILURE; + } + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + osdw, 5, 0, do_osd_write, + "write 16-bit hex encoded buffer to osd memory", + "osdw [pos_x] [pos_y] [buffer] [count] - write 8-bit hex encoded buffer to osd memory\n" +); + +U_BOOT_CMD( + osdp, 5, 0, do_osd_print, + "write ASCII buffer to osd memory", + "osdp [pos_x] [pos_y] [color] [text] - write ASCII buffer to osd memory\n" +); + +U_BOOT_CMD( + osdsize, 3, 0, do_osd_size, + "set OSD XY size in characters", + "osdsize [size_x] [size_y] - set OSD XY size in characters\n" +); diff --git a/board/gdsys/mpc8308/Kconfig b/board/gdsys/mpc8308/Kconfig index cb29c25c650..9d99f686923 100644 --- a/board/gdsys/mpc8308/Kconfig +++ b/board/gdsys/mpc8308/Kconfig @@ -1,3 +1,9 @@ +config GDSYS_LEGACY_OSD_CMDS + bool + help + Use the 'osdw', 'osdp', and 'osdsize' legacy commands required by + gdsys devices. + if TARGET_HRCON config SYS_BOARD @@ -9,6 +15,9 @@ config SYS_VENDOR config SYS_CONFIG_NAME default "hrcon" +config GDSYS_LEGACY_OSD_CMDS + default y + endif if TARGET_STRIDER @@ -22,6 +31,8 @@ config SYS_VENDOR config SYS_CONFIG_NAME default "strider" +config GDSYS_LEGACY_OSD_CMDS + default y endif config CMD_IOLOOP diff --git a/cmd/Kconfig b/cmd/Kconfig index cf97a0f2be3..442b90a9d3f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -913,6 +913,14 @@ config CMD_ONENAND and erasing blocks. It allso provides a way to show and change bad blocks, and test the device. +config CMD_OSD + bool "osd" + help + Enable the 'osd' command which allows to query information from and + write text data to a on-screen display (OSD) device; a virtual device + associated with a display capable of displaying a text overlay on the + display it's associated with.. + config CMD_PART bool "part" select HAVE_BLOCK_DEVICE diff --git a/cmd/Makefile b/cmd/Makefile index d3815abf267..8f91570306d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_NET) += net.o obj-$(CONFIG_CMD_ONENAND) += onenand.o +obj-$(CONFIG_CMD_OSD) += osd.o obj-$(CONFIG_CMD_PART) += part.o ifdef CONFIG_PCI obj-$(CONFIG_CMD_PCI) += pci.o diff --git a/cmd/osd.c b/cmd/osd.c new file mode 100644 index 00000000000..0b1fa499368 --- /dev/null +++ b/cmd/osd.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + */ + +#include +#include +#include +#include +#include + +/* Container for selected OSD device */ +static struct udevice *osd_cur; + +/** + * cmd_osd_set_osd_num() - Set the OSD selected for operation + * + * Set the OSD device, which will be used by all subsequent OSD commands. + * + * Devices are identified by their uclass sequence number (as listed by 'osd + * show'). + * + * @osdnum: The OSD device to be selected, identified by its sequence number. + * Return: 0 if OK, -ve on error + */ +static int cmd_osd_set_osd_num(unsigned int osdnum) +{ + struct udevice *osd; + int res; + + res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, osdnum, &osd); + if (res) { + printf("%s: No OSD %u (err = %d)\n", __func__, osdnum, res); + return res; + } + osd_cur = osd; + + return 0; +} + +/** + * osd_get_osd_cur() - Get the selected OSD device + * + * Get the OSD device that is used by all OSD commands. + * + * @osdp: Pointer to structure that will receive the currently selected OSD + * device. + * Return: 0 if OK, -ve on error + */ +static int osd_get_osd_cur(struct udevice **osdp) +{ + if (!osd_cur) { + puts("No osd selected\n"); + return -ENODEV; + } + *osdp = osd_cur; + + return 0; +} + +/** + * show_osd() - Display information about a OSD device + * + * Display a device's ID (sequence number), and whether it is active (i.e. + * probed) or not. + * + * @osd: OSD device to print information for + */ +static void show_osd(struct udevice *osd) +{ + printf("OSD %d:\t%s", osd->req_seq, osd->name); + if (device_active(osd)) + printf(" (active %d)", osd->seq); + printf("\n"); +} + +static int do_osd_write(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + uint x, y; + uint count; + char *hexstr; + u8 *buffer; + size_t buflen; + int res; + + if (argc < 4 || (strlen(argv[3]) % 2)) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + hexstr = argv[3]; + count = (argc > 4) ? simple_strtoul(argv[4], NULL, 16) : 1; + + buflen = strlen(hexstr) / 2; + + buffer = malloc(buflen); + if (!buffer) { + puts("Memory allocation failure\n"); + return CMD_RET_FAILURE; + } + + res = hex2bin(buffer, hexstr, buflen); + if (res) { + free(buffer); + puts("Hexadecimal input contained invalid characters\n"); + return CMD_RET_FAILURE; + } + + res = video_osd_set_mem(osd_cur, x, y, buffer, buflen, count); + if (res) { + free(buffer); + printf("%s: Could not write to video mem\n", + osd_cur->name); + return CMD_RET_FAILURE; + } + + free(buffer); + + return CMD_RET_SUCCESS; +} + +static int do_osd_print(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + uint x, y; + u8 color; + char *text; + int res; + + if (argc < 5) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + color = simple_strtoul(argv[3], NULL, 16); + text = argv[4]; + + res = video_osd_print(osd_cur, x, y, color, text); + if (res) { + printf("Could not print string to osd %s\n", osd_cur->name); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_osd_size(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + uint x, y; + int res; + + if (argc < 3) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + + res = video_osd_set_size(osd_cur, x, y); + if (res) { + printf("Could not set size on osd %s\n", osd_cur->name); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_show_osd(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *osd; + + if (argc == 1) { + /* show all OSDs */ + struct uclass *uc; + int res; + + res = uclass_get(UCLASS_VIDEO_OSD, &uc); + if (res) { + printf("Error while getting OSD uclass (err=%d)\n", + res); + return CMD_RET_FAILURE; + } + + uclass_foreach_dev(osd, uc) + show_osd(osd); + } else { + int i, res; + + /* show specific OSD */ + i = simple_strtoul(argv[1], NULL, 10); + + res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, i, &osd); + if (res) { + printf("Invalid osd %d: err=%d\n", i, res); + return CMD_RET_FAILURE; + } + show_osd(osd); + } + + return CMD_RET_SUCCESS; +} + +static int do_osd_num(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int osd_no; + int res = 0; + + if (argc == 1) { + /* querying current setting */ + struct udevice *osd; + + if (!osd_get_osd_cur(&osd)) + osd_no = osd->seq; + else + osd_no = -1; + printf("Current osd is %d\n", osd_no); + } else { + osd_no = simple_strtoul(argv[1], NULL, 10); + printf("Setting osd to %d\n", osd_no); + + res = cmd_osd_set_osd_num(osd_no); + if (res) + printf("Failure changing osd number (err = %d)\n", res); + } + + return res ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static cmd_tbl_t cmd_osd_sub[] = { + U_BOOT_CMD_MKENT(show, 1, 1, do_show_osd, "", ""), + U_BOOT_CMD_MKENT(dev, 1, 1, do_osd_num, "", ""), + U_BOOT_CMD_MKENT(write, 4, 1, do_osd_write, "", ""), + U_BOOT_CMD_MKENT(print, 4, 1, do_osd_print, "", ""), + U_BOOT_CMD_MKENT(size, 2, 1, do_osd_size, "", ""), +}; + +static int do_osd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'osd' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_osd_sub[0], ARRAY_SIZE(cmd_osd_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +static char osd_help_text[] = + "show - show OSD info\n" + "osd dev [dev] - show or set current OSD\n" + "write [pos_x] [pos_y] [buffer] [count] - write 8-bit hex encoded buffer to osd memory at a given position\n" + "print [pos_x] [pos_y] [color] [text] - write ASCII buffer (given by text data and driver-specific color information) to osd memory\n" + "size [size_x] [size_y] - set OSD XY size in characters\n"; + +U_BOOT_CMD( + osd, 6, 1, do_osd, + "OSD sub-system", + osd_help_text +); -- cgit v1.2.3 From 47b11c814ed75af32e4c1fe30d1f641080700c13 Mon Sep 17 00:00:00 2001 From: Sébastien Szymanski Date: Mon, 10 Sep 2018 09:58:58 +0200 Subject: video: cfb_console: flush cache in display_rle8_bitmap Otherwise BMP RLE8 images are not properly displayed. Signed-off-by: Sébastien Szymanski --- drivers/video/cfb_console.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 40110668a6b..636c3e8c184 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -1297,6 +1297,10 @@ next_run: break; } } + + if (cfb_do_flush_cache) + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); + return 0; error: printf("Error: Too much encoded pixel data, validate your bitmap\n"); -- cgit v1.2.3 From cc0dae08c69a0a2a1772d8358e20020d2829910a Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Mon, 17 Sep 2018 17:45:03 +0100 Subject: include/video.h: Remove declaration of functions that don't exist. video_init, video_putc and video_puts functions are not implemented anywhere, remove their declaration from the header. Signed-off-by: Liviu Dudau Reviewed-by: Simon Glass Reviewed-by: Heiko Schocher --- include/video.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/video.h b/include/video.h index ddc2eeb5a95..e7fc5c94e2e 100644 --- a/include/video.h +++ b/include/video.h @@ -198,12 +198,6 @@ void video_set_default_colors(struct video_priv *priv); /* Video functions */ -struct stdio_dev; - -int video_init(void *videobase); -void video_putc(struct stdio_dev *dev, const char c); -void video_puts(struct stdio_dev *dev, const char *s); - /** * Display a BMP format bitmap on the screen * -- cgit v1.2.3 From 055da186a1d83d7d73d6d5b08b23957d3fa3df9f Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Fri, 28 Sep 2018 13:49:31 +0100 Subject: video: Add support for NXP's TDA19988 HDMI encoder Add support for the NXP TDA19988 HDMI encoder as used on the Juno development board from Arm. Signed-off-by: Liviu Dudau --- MAINTAINERS | 5 + drivers/video/Kconfig | 8 + drivers/video/Makefile | 1 + drivers/video/tda19988.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 667 insertions(+) create mode 100644 drivers/video/tda19988.c diff --git a/MAINTAINERS b/MAINTAINERS index 3c3f8722431..072e73ed600 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -589,6 +589,11 @@ S: Maintained F: drivers/spmi/ F: include/spmi/ +TDA19988 HDMI ENCODER +M: Liviu Dudau +S: Maintained +F: drivers/video/tda19988.c + TI SYSTEM SECURITY M: Andrew F. Davis S: Supported diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c68066692d7..6cd7f144b73 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -431,6 +431,14 @@ config DISPLAY The devices provide a simple interface to start up the display, read display information and enable it. +config NXP_TDA19988 + bool "Enable NXP TDA19988 support" + depends on DISPLAY + default n + help + This enables support for the NXP TDA19988 HDMI encoder. This encoder + will convert RGB data streams into HDMI-encoded signals. + config ATMEL_HLCD bool "Enable ATMEL video support using HLCDC" depends on DM_VIDEO diff --git a/drivers/video/Makefile b/drivers/video/Makefile index b31017e436d..6f226576547 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_IHS_VIDEO_OUT) += ihs_video_out.o obj-$(CONFIG_LD9040) += ld9040.o obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o +obj-$(CONFIG_NXP_TDA19988) += tda19988.o obj-$(CONFIG_OSD) += video_osd-uclass.o obj-$(CONFIG_PXA_LCD) += pxa_lcd.o obj-$(CONFIG_SANDBOX_OSD) += sandbox_osd.o diff --git a/drivers/video/tda19988.c b/drivers/video/tda19988.c new file mode 100644 index 00000000000..01ed6193ea4 --- /dev/null +++ b/drivers/video/tda19988.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Liviu Dudau + * + * Based on the Linux driver, (C) 2012 Texas Instruments + */ + +#include +#include +#include +#include + +/* + * TDA19988 uses paged registers. We encode the page# in the upper + * bits of the register#. It also means that reads/writes to a register + * have to ensure that the register's page is selected as the current + * page. + */ +#define REG(page, addr) (((page) << 8) | (addr)) +#define REG2ADDR(reg) ((reg) & 0xff) +#define REG2PAGE(reg) (((reg) >> 8) & 0xff) + +/* register for setting current page */ +#define REG_CURRENT_PAGE 0xff + +/* Page 00h: General Control */ +#define REG_VERSION_LSB REG(0x00, 0x00) /* read */ +#define REG_MAIN_CNTRL0 REG(0x00, 0x01) /* read/write */ +#define MAIN_CNTRL0_SR BIT(0) +#define MAIN_CNTRL0_DECS BIT(1) +#define MAIN_CNTRL0_DEHS BIT(2) +#define MAIN_CNTRL0_CECS BIT(3) +#define MAIN_CNTRL0_CEHS BIT(4) +#define MAIN_CNTRL0_SCALER BIT(7) +#define REG_VERSION_MSB REG(0x00, 0x02) /* read */ +#define REG_SOFTRESET REG(0x00, 0x0a) /* write */ +#define SOFTRESET_AUDIO BIT(0) +#define SOFTRESET_I2C_MASTER BIT(1) +#define REG_DDC_DISABLE REG(0x00, 0x0b) /* read/write */ +#define REG_I2C_MASTER REG(0x00, 0x0d) /* read/write */ +#define I2C_MASTER_DIS_MM BIT(0) +#define I2C_MASTER_DIS_FILT BIT(1) +#define I2C_MASTER_APP_STRT_LAT BIT(2) +#define REG_FEAT_POWERDOWN REG(0x00, 0x0e) /* read/write */ +#define FEAT_POWERDOWN_PREFILT BIT(0) +#define FEAT_POWERDOWN_CSC BIT(1) +#define FEAT_POWERDOWN_SPDIF BIT(3) +#define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ +#define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ +#define REG_INT_FLAGS_2 REG(0x00, 0x11) /* read/write */ +#define INT_FLAGS_2_EDID_BLK_RD BIT(1) +#define REG_ENA_VP_0 REG(0x00, 0x18) /* read/write */ +#define REG_ENA_VP_1 REG(0x00, 0x19) /* read/write */ +#define REG_ENA_VP_2 REG(0x00, 0x1a) /* read/write */ +#define REG_ENA_AP REG(0x00, 0x1e) /* read/write */ +#define REG_VIP_CNTRL_0 REG(0x00, 0x20) /* write */ +#define VIP_CNTRL_0_MIRR_A BIT(7) +#define VIP_CNTRL_0_SWAP_A(x) (((x) & 7) << 4) +#define VIP_CNTRL_0_MIRR_B BIT(3) +#define VIP_CNTRL_0_SWAP_B(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_1 REG(0x00, 0x21) /* write */ +#define VIP_CNTRL_1_MIRR_C BIT(7) +#define VIP_CNTRL_1_SWAP_C(x) (((x) & 7) << 4) +#define VIP_CNTRL_1_MIRR_D BIT(3) +#define VIP_CNTRL_1_SWAP_D(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_2 REG(0x00, 0x22) /* write */ +#define VIP_CNTRL_2_MIRR_E BIT(7) +#define VIP_CNTRL_2_SWAP_E(x) (((x) & 7) << 4) +#define VIP_CNTRL_2_MIRR_F BIT(3) +#define VIP_CNTRL_2_SWAP_F(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_3 REG(0x00, 0x23) /* write */ +#define VIP_CNTRL_3_X_TGL BIT(0) +#define VIP_CNTRL_3_H_TGL BIT(1) +#define VIP_CNTRL_3_V_TGL BIT(2) +#define VIP_CNTRL_3_EMB BIT(3) +#define VIP_CNTRL_3_SYNC_DE BIT(4) +#define VIP_CNTRL_3_SYNC_HS BIT(5) +#define VIP_CNTRL_3_DE_INT BIT(6) +#define VIP_CNTRL_3_EDGE BIT(7) +#define REG_VIP_CNTRL_4 REG(0x00, 0x24) /* write */ +#define VIP_CNTRL_4_BLC(x) (((x) & 3) << 0) +#define VIP_CNTRL_4_BLANKIT(x) (((x) & 3) << 2) +#define VIP_CNTRL_4_CCIR656 BIT(4) +#define VIP_CNTRL_4_656_ALT BIT(5) +#define VIP_CNTRL_4_TST_656 BIT(6) +#define VIP_CNTRL_4_TST_PAT BIT(7) +#define REG_VIP_CNTRL_5 REG(0x00, 0x25) /* write */ +#define VIP_CNTRL_5_CKCASE BIT(0) +#define VIP_CNTRL_5_SP_CNT(x) (((x) & 3) << 1) +#define REG_MUX_VP_VIP_OUT REG(0x00, 0x27) /* read/write */ +#define REG_MAT_CONTRL REG(0x00, 0x80) /* write */ +#define MAT_CONTRL_MAT_SC(x) (((x) & 3) << 0) +#define MAT_CONTRL_MAT_BP BIT(2) +#define REG_VIDFORMAT REG(0x00, 0xa0) /* write */ +#define REG_REFPIX_MSB REG(0x00, 0xa1) /* write */ +#define REG_REFPIX_LSB REG(0x00, 0xa2) /* write */ +#define REG_REFLINE_MSB REG(0x00, 0xa3) /* write */ +#define REG_REFLINE_LSB REG(0x00, 0xa4) /* write */ +#define REG_NPIX_MSB REG(0x00, 0xa5) /* write */ +#define REG_NPIX_LSB REG(0x00, 0xa6) /* write */ +#define REG_NLINE_MSB REG(0x00, 0xa7) /* write */ +#define REG_NLINE_LSB REG(0x00, 0xa8) /* write */ +#define REG_VS_LINE_STRT_1_MSB REG(0x00, 0xa9) /* write */ +#define REG_VS_LINE_STRT_1_LSB REG(0x00, 0xaa) /* write */ +#define REG_VS_PIX_STRT_1_MSB REG(0x00, 0xab) /* write */ +#define REG_VS_PIX_STRT_1_LSB REG(0x00, 0xac) /* write */ +#define REG_VS_LINE_END_1_MSB REG(0x00, 0xad) /* write */ +#define REG_VS_LINE_END_1_LSB REG(0x00, 0xae) /* write */ +#define REG_VS_PIX_END_1_MSB REG(0x00, 0xaf) /* write */ +#define REG_VS_PIX_END_1_LSB REG(0x00, 0xb0) /* write */ +#define REG_VS_LINE_STRT_2_MSB REG(0x00, 0xb1) /* write */ +#define REG_VS_LINE_STRT_2_LSB REG(0x00, 0xb2) /* write */ +#define REG_VS_PIX_STRT_2_MSB REG(0x00, 0xb3) /* write */ +#define REG_VS_PIX_STRT_2_LSB REG(0x00, 0xb4) /* write */ +#define REG_VS_LINE_END_2_MSB REG(0x00, 0xb5) /* write */ +#define REG_VS_LINE_END_2_LSB REG(0x00, 0xb6) /* write */ +#define REG_VS_PIX_END_2_MSB REG(0x00, 0xb7) /* write */ +#define REG_VS_PIX_END_2_LSB REG(0x00, 0xb8) /* write */ +#define REG_HS_PIX_START_MSB REG(0x00, 0xb9) /* write */ +#define REG_HS_PIX_START_LSB REG(0x00, 0xba) /* write */ +#define REG_HS_PIX_STOP_MSB REG(0x00, 0xbb) /* write */ +#define REG_HS_PIX_STOP_LSB REG(0x00, 0xbc) /* write */ +#define REG_VWIN_START_1_MSB REG(0x00, 0xbd) /* write */ +#define REG_VWIN_START_1_LSB REG(0x00, 0xbe) /* write */ +#define REG_VWIN_END_1_MSB REG(0x00, 0xbf) /* write */ +#define REG_VWIN_END_1_LSB REG(0x00, 0xc0) /* write */ +#define REG_VWIN_START_2_MSB REG(0x00, 0xc1) /* write */ +#define REG_VWIN_START_2_LSB REG(0x00, 0xc2) /* write */ +#define REG_VWIN_END_2_MSB REG(0x00, 0xc3) /* write */ +#define REG_VWIN_END_2_LSB REG(0x00, 0xc4) /* write */ +#define REG_DE_START_MSB REG(0x00, 0xc5) /* write */ +#define REG_DE_START_LSB REG(0x00, 0xc6) /* write */ +#define REG_DE_STOP_MSB REG(0x00, 0xc7) /* write */ +#define REG_DE_STOP_LSB REG(0x00, 0xc8) /* write */ +#define REG_TBG_CNTRL_0 REG(0x00, 0xca) /* write */ +#define TBG_CNTRL_0_TOP_TGL BIT(0) +#define TBG_CNTRL_0_TOP_SEL BIT(1) +#define TBG_CNTRL_0_DE_EXT BIT(2) +#define TBG_CNTRL_0_TOP_EXT BIT(3) +#define TBG_CNTRL_0_FRAME_DIS BIT(5) +#define TBG_CNTRL_0_SYNC_MTHD BIT(6) +#define TBG_CNTRL_0_SYNC_ONCE BIT(7) +#define REG_TBG_CNTRL_1 REG(0x00, 0xcb) /* write */ +#define TBG_CNTRL_1_H_TGL BIT(0) +#define TBG_CNTRL_1_V_TGL BIT(1) +#define TBG_CNTRL_1_TGL_EN BIT(2) +#define TBG_CNTRL_1_X_EXT BIT(3) +#define TBG_CNTRL_1_H_EXT BIT(4) +#define TBG_CNTRL_1_V_EXT BIT(5) +#define TBG_CNTRL_1_DWIN_DIS BIT(6) +#define REG_ENABLE_SPACE REG(0x00, 0xd6) /* write */ +#define REG_HVF_CNTRL_0 REG(0x00, 0xe4) /* write */ +#define HVF_CNTRL_0_SM BIT(7) +#define HVF_CNTRL_0_RWB BIT(6) +#define HVF_CNTRL_0_PREFIL(x) (((x) & 3) << 2) +#define HVF_CNTRL_0_INTPOL(x) (((x) & 3) << 0) +#define REG_HVF_CNTRL_1 REG(0x00, 0xe5) /* write */ +#define HVF_CNTRL_1_FOR BIT(0) +#define HVF_CNTRL_1_YUVBLK BIT(1) +#define HVF_CNTRL_1_VQR(x) (((x) & 3) << 2) +#define HVF_CNTRL_1_PAD(x) (((x) & 3) << 4) +#define REG_RPT_CNTRL REG(0x00, 0xf0) /* write */ +#define REG_AIP_CLKSEL REG(0x00, 0xfd) /* write */ +#define AIP_CLKSEL_AIP_SPDIF (0 << 3) +#define AIP_CLKSEL_AIP_I2S BIT(3) +#define AIP_CLKSEL_FS_ACLK (0 << 0) +#define AIP_CLKSEL_FS_MCLK BIT(0) + +/* Page 02h: PLL settings */ +#define REG_PLL_SERIAL_1 REG(0x02, 0x00) /* read/write */ +#define PLL_SERIAL_1_SRL_FDN BIT(0) +#define PLL_SERIAL_1_SRL_IZ(x) (((x) & 3) << 1) +#define PLL_SERIAL_1_SRL_MAN_IZ BIT(6) +#define REG_PLL_SERIAL_2 REG(0x02, 0x01) /* read/write */ +#define PLL_SERIAL_2_SRL_NOSC(x) ((x) << 0) +#define PLL_SERIAL_2_SRL_PR(x) (((x) & 0xf) << 4) +#define REG_PLL_SERIAL_3 REG(0x02, 0x02) /* read/write */ +#define PLL_SERIAL_3_SRL_CCIR BIT(0) +#define PLL_SERIAL_3_SRL_DE BIT(2) +#define PLL_SERIAL_3_SRL_PXIN_SEL BIT(4) +#define REG_SERIALIZER REG(0x02, 0x03) /* read/write */ +#define REG_BUFFER_OUT REG(0x02, 0x04) /* read/write */ +#define REG_PLL_SCG1 REG(0x02, 0x05) /* read/write */ +#define REG_PLL_SCG2 REG(0x02, 0x06) /* read/write */ +#define REG_PLL_SCGN1 REG(0x02, 0x07) /* read/write */ +#define REG_PLL_SCGN2 REG(0x02, 0x08) /* read/write */ +#define REG_PLL_SCGR1 REG(0x02, 0x09) /* read/write */ +#define REG_PLL_SCGR2 REG(0x02, 0x0a) /* read/write */ +#define REG_AUDIO_DIV REG(0x02, 0x0e) /* read/write */ +#define AUDIO_DIV_SERCLK_1 0 +#define AUDIO_DIV_SERCLK_2 1 +#define AUDIO_DIV_SERCLK_4 2 +#define AUDIO_DIV_SERCLK_8 3 +#define AUDIO_DIV_SERCLK_16 4 +#define AUDIO_DIV_SERCLK_32 5 +#define REG_SEL_CLK REG(0x02, 0x11) /* read/write */ +#define SEL_CLK_SEL_CLK1 BIT(0) +#define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1) +#define SEL_CLK_ENA_SC_CLK BIT(3) +#define REG_ANA_GENERAL REG(0x02, 0x12) /* read/write */ + +/* Page 09h: EDID Control */ +#define REG_EDID_DATA_0 REG(0x09, 0x00) /* read */ +/* next 127 successive registers are the EDID block */ +#define REG_EDID_CTRL REG(0x09, 0xfa) /* read/write */ +#define REG_DDC_ADDR REG(0x09, 0xfb) /* read/write */ +#define REG_DDC_OFFS REG(0x09, 0xfc) /* read/write */ +#define REG_DDC_SEGM_ADDR REG(0x09, 0xfd) /* read/write */ +#define REG_DDC_SEGM REG(0x09, 0xfe) /* read/write */ + +/* Page 11h: audio settings and content info packets */ +#define REG_AIP_CNTRL_0 REG(0x11, 0x00) /* read/write */ +#define AIP_CNTRL_0_RST_FIFO BIT(0) +#define REG_ENC_CNTRL REG(0x11, 0x0d) /* read/write */ +#define ENC_CNTRL_RST_ENC BIT(0) +#define ENC_CNTRL_RST_SEL BIT(1) +#define ENC_CNTRL_CTL_CODE(x) (((x) & 3) << 2) + +/* Page 12h: HDCP and OTP */ +#define REG_TX3 REG(0x12, 0x9a) /* read/write */ +#define REG_TX4 REG(0x12, 0x9b) /* read/write */ +#define TX4_PD_RAM BIT(1) +#define REG_TX33 REG(0x12, 0xb8) /* read/write */ +#define TX33_HDMI BIT(1) + +/* CEC registers, not paged */ +#define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */ +#define CEC_FRO_IM_CLK_CTRL_GHOST_DIS BIT(7) +#define CEC_FRO_IM_CLK_CTRL_ENA_OTP BIT(6) +#define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL BIT(1) +#define CEC_FRO_IM_CLK_CTRL_FRO_DIV BIT(0) +#define REG_CEC_RXSHPDINTENA 0xfc /* read/write */ +#define REG_CEC_RXSHPDINT 0xfd /* read */ +#define CEC_RXSHPDINT_RXSENS BIT(0) +#define CEC_RXSHPDINT_HPD BIT(1) +#define TDA19988_CEC_ENAMODS 0xff /* read/write */ +#define CEC_ENAMODS_EN_RXSENS BIT(2) +#define CEC_ENAMODS_EN_HDMI BIT(1) +#define CEC_ENAMODS_EN_CEC BIT(0) + +/* Device versions */ +#define TDA9989N2 0x0101 +#define TDA19989 0x0201 +#define TDA19989N2 0x0202 +#define TDA19988 0x0301 + +struct tda19988_priv { + struct udevice *chip; + struct udevice *cec_chip; + u16 revision; + u8 current_page; +}; + +static void tda19988_register_set(struct tda19988_priv *priv, u16 reg, u8 val) +{ + u8 old_val, page = REG2PAGE(reg); + + if (priv->current_page != page) { + dm_i2c_reg_write(priv->chip, REG_CURRENT_PAGE, page); + priv->current_page = page; + } + old_val = dm_i2c_reg_read(priv->chip, REG2ADDR(reg)); + old_val |= val; + dm_i2c_reg_write(priv->chip, REG2ADDR(reg), old_val); +} + +static void tda19988_register_clear(struct tda19988_priv *priv, u16 reg, u8 val) +{ + u8 old_val, page = REG2PAGE(reg); + + if (priv->current_page != page) { + dm_i2c_reg_write(priv->chip, REG_CURRENT_PAGE, page); + priv->current_page = page; + } + old_val = dm_i2c_reg_read(priv->chip, REG2ADDR(reg)); + old_val &= ~val; + dm_i2c_reg_write(priv->chip, REG2ADDR(reg), old_val); +} + +static void tda19988_register_write(struct tda19988_priv *priv, u16 reg, u8 val) +{ + u8 page = REG2PAGE(reg); + + if (priv->current_page != page) { + dm_i2c_reg_write(priv->chip, REG_CURRENT_PAGE, page); + priv->current_page = page; + } + dm_i2c_reg_write(priv->chip, REG2ADDR(reg), val); +} + +static int tda19988_register_read(struct tda19988_priv *priv, u16 reg) +{ + u8 page = REG2PAGE(reg); + + if (priv->current_page != page) { + dm_i2c_reg_write(priv->chip, REG_CURRENT_PAGE, page); + priv->current_page = page; + } + return dm_i2c_reg_read(priv->chip, REG2ADDR(reg)); +} + +static void tda19988_register_write16(struct tda19988_priv *priv, + u16 reg, u16 val) +{ + u8 buf[] = { val >> 8, val }, page = REG2PAGE(reg); + + if (priv->current_page != page) { + dm_i2c_reg_write(priv->chip, REG_CURRENT_PAGE, page); + priv->current_page = page; + } + dm_i2c_write(priv->chip, REG2ADDR(reg), buf, 2); +} + +static int tda19988_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct tda19988_priv *priv = dev_get_priv(dev); + int i, val = 0, offset = 0; + + /* + * The TDA998x has a problem when trying to read the EDID close to a + * HPD assertion: it needs a delay of 100ms to avoid timing out while + * trying to read EDID data. + */ + mdelay(120); + + if (priv->revision == TDA19988) + tda19988_register_clear(priv, REG_TX4, TX4_PD_RAM); + + while (offset < buf_size) { + tda19988_register_write(priv, REG_DDC_ADDR, 0xa0); + tda19988_register_write(priv, REG_DDC_OFFS, offset); + tda19988_register_write(priv, REG_DDC_SEGM_ADDR, 0x60); + tda19988_register_write(priv, REG_DDC_SEGM, 0); + + /* enable reading EDID */ + tda19988_register_write(priv, REG_EDID_CTRL, 1); + + /* flags must be cleared by software */ + tda19988_register_write(priv, REG_EDID_CTRL, 0); + + /* wait for block read to complete */ + for (i = 300; i > 0; i--) { + mdelay(1); + val = tda19988_register_read(priv, REG_INT_FLAGS_2); + if (val < 0) + return val; + if (val & INT_FLAGS_2_EDID_BLK_RD) + break; + } + + if (i == 0) + return -ETIMEDOUT; + + priv->current_page = REG2PAGE(REG_EDID_DATA_0); + dm_i2c_reg_write(priv->chip, + REG_CURRENT_PAGE, REG2PAGE(REG_EDID_DATA_0)); + val = dm_i2c_read(priv->chip, + REG2ADDR(REG_EDID_DATA_0), buf + offset, 128); + offset += 128; + } + + if (priv->revision == TDA19988) + tda19988_register_set(priv, REG_TX4, TX4_PD_RAM); + + return offset; +} + +static int tda19988_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct tda19988_priv *priv = dev_get_priv(dev); + u8 div = 148500000 / timing->pixelclock.typ, reg; + u16 line_clocks, lines; + + if (dev != 0) { + div--; + if (div > 3) + div = 3; + } + /* first disable the video ports */ + tda19988_register_write(priv, REG_ENA_VP_0, 0); + tda19988_register_write(priv, REG_ENA_VP_1, 0); + tda19988_register_write(priv, REG_ENA_VP_2, 0); + + /* shutdown audio */ + tda19988_register_write(priv, REG_ENA_AP, 0); + + line_clocks = timing->hsync_len.typ + timing->hback_porch.typ + + timing->hactive.typ + timing->hfront_porch.typ; + lines = timing->vsync_len.typ + timing->vback_porch.typ + + timing->vactive.typ + timing->vfront_porch.typ; + + /* mute the audio FIFO */ + tda19988_register_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); + /* HDMI HDCP: off */ + tda19988_register_write(priv, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); + tda19988_register_clear(priv, REG_TX33, TX33_HDMI); + tda19988_register_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0)); + + /* no pre-filter or interpolator */ + tda19988_register_write(priv, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) | + HVF_CNTRL_0_INTPOL(0)); + tda19988_register_set(priv, REG_FEAT_POWERDOWN, + FEAT_POWERDOWN_PREFILT); + tda19988_register_write(priv, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0)); + tda19988_register_write(priv, REG_VIP_CNTRL_4, + VIP_CNTRL_4_BLANKIT(0) | VIP_CNTRL_4_BLC(0) | + VIP_CNTRL_4_TST_PAT); + + tda19988_register_clear(priv, REG_PLL_SERIAL_1, + PLL_SERIAL_1_SRL_MAN_IZ); + tda19988_register_clear(priv, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR | + PLL_SERIAL_3_SRL_DE); + + tda19988_register_write(priv, REG_SERIALIZER, 0); + tda19988_register_write(priv, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0)); + + tda19988_register_write(priv, REG_RPT_CNTRL, 0); + tda19988_register_write(priv, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | + SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + tda19988_register_write(priv, REG_PLL_SERIAL_2, + PLL_SERIAL_2_SRL_NOSC(div) | + PLL_SERIAL_2_SRL_PR(0)); + + /* set color matrix bypass flag: */ + tda19988_register_write(priv, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP | + MAT_CONTRL_MAT_SC(1)); + tda19988_register_set(priv, REG_FEAT_POWERDOWN, FEAT_POWERDOWN_CSC); + + /* set BIAS tmds value: */ + tda19988_register_write(priv, REG_ANA_GENERAL, 0x09); + + /* + * Sync on rising HSYNC/VSYNC + */ + reg = VIP_CNTRL_3_SYNC_HS; + + /* + * TDA19988 requires high-active sync at input stage, + * so invert low-active sync provided by master encoder here + */ + if (timing->flags & DISPLAY_FLAGS_HSYNC_LOW) + reg |= VIP_CNTRL_3_H_TGL; + if (timing->flags & DISPLAY_FLAGS_VSYNC_LOW) + reg |= VIP_CNTRL_3_V_TGL; + tda19988_register_write(priv, REG_VIP_CNTRL_3, reg); + + tda19988_register_write(priv, REG_VIDFORMAT, 0x00); + tda19988_register_write16(priv, REG_REFPIX_MSB, + timing->hfront_porch.typ + 3); + tda19988_register_write16(priv, REG_REFLINE_MSB, + timing->vfront_porch.typ + 1); + tda19988_register_write16(priv, REG_NPIX_MSB, line_clocks); + tda19988_register_write16(priv, REG_NLINE_MSB, lines); + tda19988_register_write16(priv, REG_VS_LINE_STRT_1_MSB, + timing->vfront_porch.typ); + tda19988_register_write16(priv, REG_VS_PIX_STRT_1_MSB, + timing->hfront_porch.typ); + tda19988_register_write16(priv, REG_VS_LINE_END_1_MSB, + timing->vfront_porch.typ + + timing->vsync_len.typ); + tda19988_register_write16(priv, REG_VS_PIX_END_1_MSB, + timing->hfront_porch.typ); + tda19988_register_write16(priv, REG_VS_LINE_STRT_2_MSB, 0); + tda19988_register_write16(priv, REG_VS_PIX_STRT_2_MSB, 0); + tda19988_register_write16(priv, REG_VS_LINE_END_2_MSB, 0); + tda19988_register_write16(priv, REG_VS_PIX_END_2_MSB, 0); + tda19988_register_write16(priv, REG_HS_PIX_START_MSB, + timing->hfront_porch.typ); + tda19988_register_write16(priv, REG_HS_PIX_STOP_MSB, + timing->hfront_porch.typ + + timing->hsync_len.typ); + tda19988_register_write16(priv, REG_VWIN_START_1_MSB, + lines - timing->vactive.typ - 1); + tda19988_register_write16(priv, REG_VWIN_END_1_MSB, lines - 1); + tda19988_register_write16(priv, REG_VWIN_START_2_MSB, 0); + tda19988_register_write16(priv, REG_VWIN_END_2_MSB, 0); + tda19988_register_write16(priv, REG_DE_START_MSB, + line_clocks - timing->hactive.typ); + tda19988_register_write16(priv, REG_DE_STOP_MSB, line_clocks); + + if (priv->revision == TDA19988) { + /* let incoming pixels fill the active space (if any) */ + tda19988_register_write(priv, REG_ENABLE_SPACE, 0x00); + } + + /* + * Always generate sync polarity relative to input sync and + * revert input stage toggled sync at output stage + */ + reg = TBG_CNTRL_1_DWIN_DIS | TBG_CNTRL_1_TGL_EN; + if (timing->flags & DISPLAY_FLAGS_HSYNC_LOW) + reg |= TBG_CNTRL_1_H_TGL; + if (timing->flags & DISPLAY_FLAGS_VSYNC_LOW) + reg |= TBG_CNTRL_1_V_TGL; + tda19988_register_write(priv, REG_TBG_CNTRL_1, reg); + + /* must be last register set: */ + tda19988_register_write(priv, REG_TBG_CNTRL_0, 0); + + /* turn on HDMI HDCP */ + reg &= ~TBG_CNTRL_1_DWIN_DIS; + tda19988_register_write(priv, REG_TBG_CNTRL_1, reg); + tda19988_register_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1)); + tda19988_register_set(priv, REG_TX33, TX33_HDMI); + + mdelay(400); + + /* enable video ports */ + tda19988_register_write(priv, REG_ENA_VP_0, 0xff); + tda19988_register_write(priv, REG_ENA_VP_1, 0xff); + tda19988_register_write(priv, REG_ENA_VP_2, 0xff); + /* set muxing after enabling ports: */ + tda19988_register_write(priv, REG_VIP_CNTRL_0, + VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3)); + tda19988_register_write(priv, REG_VIP_CNTRL_1, + VIP_CNTRL_1_SWAP_C(4) | VIP_CNTRL_1_SWAP_D(5)); + tda19988_register_write(priv, REG_VIP_CNTRL_2, + VIP_CNTRL_2_SWAP_E(0) | VIP_CNTRL_2_SWAP_F(1)); + + return 0; +} + +struct dm_display_ops tda19988_ops = { + .read_edid = tda19988_read_edid, + .enable = tda19988_enable, +}; + +static const struct udevice_id tda19988_ids[] = { + { .compatible = "nxp,tda998x" }, + { } +}; + +static int tda19988_probe(struct udevice *dev) +{ + u8 cec_addr, chip_addr, rev_lo, rev_hi; + int err; + struct tda19988_priv *priv = dev_get_priv(dev); + + chip_addr = dev_read_addr(dev); + /* CEC I2C address is using TDA19988 I2C address configuration pins */ + cec_addr = 0x34 + (chip_addr & 0x03); + + err = i2c_get_chip_for_busnum(0, cec_addr, 1, &priv->cec_chip); + if (err) { + printf("cec i2c_get_chip_for_busnum returned %d\n", err); + return err; + } + + err = i2c_get_chip_for_busnum(0, chip_addr, 1, &priv->chip); + if (err) { + printf("i2c_get_chip_for_busnum returned %d\n", err); + return err; + } + + priv->current_page = 0xff; + + /* wake up device */ + dm_i2c_reg_write(priv->cec_chip, TDA19988_CEC_ENAMODS, + CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); + + /* reset audio and I2C master */ + tda19988_register_write(priv, REG_SOFTRESET, + SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER); + mdelay(50); + tda19988_register_write(priv, REG_SOFTRESET, 0); + mdelay(50); + + /* reset transmitter */ + tda19988_register_set(priv, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); + tda19988_register_clear(priv, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); + + /* PLL registers common configuration */ + tda19988_register_write(priv, REG_PLL_SERIAL_1, 0x00); + tda19988_register_write(priv, REG_PLL_SERIAL_2, + PLL_SERIAL_2_SRL_NOSC(1)); + tda19988_register_write(priv, REG_PLL_SERIAL_3, 0x00); + tda19988_register_write(priv, REG_SERIALIZER, 0x00); + tda19988_register_write(priv, REG_BUFFER_OUT, 0x00); + tda19988_register_write(priv, REG_PLL_SCG1, 0x00); + tda19988_register_write(priv, REG_AUDIO_DIV, AUDIO_DIV_SERCLK_8); + tda19988_register_write(priv, REG_SEL_CLK, + SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + tda19988_register_write(priv, REG_PLL_SCGN1, 0xfa); + tda19988_register_write(priv, REG_PLL_SCGN2, 0x00); + tda19988_register_write(priv, REG_PLL_SCGR1, 0x5b); + tda19988_register_write(priv, REG_PLL_SCGR2, 0x00); + tda19988_register_write(priv, REG_PLL_SCG2, 0x10); + + /* Write the default value MUX register */ + tda19988_register_write(priv, REG_MUX_VP_VIP_OUT, 0x24); + + /* read version */ + rev_lo = dm_i2c_reg_read(priv->chip, REG_VERSION_LSB); + rev_hi = dm_i2c_reg_read(priv->chip, REG_VERSION_MSB); + + /* mask off feature bits */ + priv->revision = ((rev_hi << 8) | rev_lo) & ~0x30; + + printf("HDMI: "); + switch (priv->revision) { + case TDA9989N2: + printf("TDA9989 n2\n"); + break; + case TDA19989: + printf("TDA19989\n"); + break; + case TDA19989N2: + printf("TDA19989 n2\n"); + break; + case TDA19988: + printf("TDA19988\n"); + break; + default: + printf("unknown TDA device: 0x%04x\n", priv->revision); + return -ENXIO; + } + + /* after reset, enable DDC */ + tda19988_register_write(priv, REG_DDC_DISABLE, 0x00); + + /* set clock on DDC channel */ + tda19988_register_write(priv, REG_TX3, 39); + + /* if necessary, disable multi-master */ + if (priv->revision == TDA19989) + tda19988_register_set(priv, REG_I2C_MASTER, I2C_MASTER_DIS_MM); + + dm_i2c_reg_write(priv->cec_chip, REG_CEC_FRO_IM_CLK_CTRL, + CEC_FRO_IM_CLK_CTRL_GHOST_DIS | + CEC_FRO_IM_CLK_CTRL_IMCLK_SEL); + /* ensure interrupts are disabled */ + dm_i2c_reg_write(priv->cec_chip, REG_CEC_RXSHPDINTENA, 0); + /* clear pending interrupts */ + dm_i2c_reg_read(priv->cec_chip, REG_CEC_RXSHPDINT); + tda19988_register_read(priv, REG_INT_FLAGS_0); + tda19988_register_read(priv, REG_INT_FLAGS_1); + tda19988_register_read(priv, REG_INT_FLAGS_2); + + /* enable EDID read irq */ + tda19988_register_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + return 0; +} + +U_BOOT_DRIVER(tda19988) = { + .name = "tda19988", + .id = UCLASS_DISPLAY, + .of_match = tda19988_ids, + .ops = &tda19988_ops, + .probe = tda19988_probe, + .priv_auto_alloc_size = sizeof(struct tda19988_priv), +}; -- cgit v1.2.3 From c1a65a8c598d27379db91cd47d30103d76311398 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Fri, 28 Sep 2018 13:50:53 +0100 Subject: video: Add support for Arm's Mali Display Processors Add support for Arm Mali Display Processors DP500, DP550 and DP650. Only one layer is being used to display the console or boot logo, even if more layers are supported in the hardware. Signed-off-by: Liviu Dudau --- MAINTAINERS | 6 + drivers/video/Kconfig | 8 + drivers/video/Makefile | 1 + drivers/video/mali_dp.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 drivers/video/mali_dp.c diff --git a/MAINTAINERS b/MAINTAINERS index 072e73ed600..45518436ff1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -440,6 +440,12 @@ F: cmd/log.c F: test/log/log_test.c F: test/py/tests/test_log.py +MALI DISPLAY PROCESSORS +M: Liviu Dudau +S: Supported +T: git git://github.com/ARM-software/u-boot.git +F: drivers/video/mali_dp.c + MICROBLAZE M: Michal Simek S: Maintained diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6cd7f144b73..25c94f4a9e3 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -497,6 +497,14 @@ config VIDEO_FSL_DCU_MAX_FB_SIZE_MB source "drivers/video/rockchip/Kconfig" +config VIDEO_ARM_MALIDP + bool "Enable Arm Mali Display Processor support" + depends on DM_VIDEO && OF_CONTROL + select VEXPRESS_CLK + help + This enables support for Arm Ltd Mali Display Processors from + the DP500, DP550 and DP650 family. + config VIDEO_SANDBOX_SDL bool "Enable sandbox video console using SDL" depends on SANDBOX diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6f226576547..80e1e829033 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_PXA_LCD) += pxa_lcd.o obj-$(CONFIG_SANDBOX_OSD) += sandbox_osd.o obj-$(CONFIG_S6E8AX0) += s6e8ax0.o obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o +obj-$(CONFIG_VIDEO_ARM_MALIDP) += mali_dp.o obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o diff --git a/drivers/video/mali_dp.c b/drivers/video/mali_dp.c new file mode 100644 index 00000000000..71151a87aac --- /dev/null +++ b/drivers/video/mali_dp.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016-2018 ARM Ltd. + * Author: Liviu Dudau + * + */ +#define DEBUG +#include +#include +#include +#ifdef CONFIG_DISPLAY +#include +#endif +#include +#include +#include +#include +#include +#include + +#define MALIDP_CORE_ID 0x0018 +#define MALIDP_REG_BG_COLOR 0x0044 +#define MALIDP_LAYER_LV1 0x0100 +#define MALIDP_DC_STATUS 0xc000 +#define MALIDP_DC_CONTROL 0xc010 +#define MALIDP_DC_CFG_VALID 0xc014 + +/* offsets inside the modesetting register block */ +#define MALIDP_H_INTERVALS 0x0000 +#define MALIDP_V_INTERVALS 0x0004 +#define MALIDP_SYNC_CONTROL 0x0008 +#define MALIDP_HV_ACTIVESIZE 0x000c +#define MALIDP_OUTPUT_DEPTH 0x001c + +/* offsets inside the layer register block */ +#define MALIDP_LAYER_FORMAT 0x0000 +#define MALIDP_LAYER_CONTROL 0x0004 +#define MALIDP_LAYER_IN_SIZE 0x000c +#define MALIDP_LAYER_CMP_SIZE 0x0010 +#define MALIDP_LAYER_STRIDE 0x0018 +#define MALIDP_LAYER_PTR_LOW 0x0024 +#define MALIDP_LAYER_PTR_HIGH 0x0028 + +/* offsets inside the IRQ control blocks */ +#define MALIDP_REG_MASKIRQ 0x0008 +#define MALIDP_REG_CLEARIRQ 0x000c + +#define M1BITS 0x0001 +#define M2BITS 0x0003 +#define M4BITS 0x000f +#define M8BITS 0x00ff +#define M10BITS 0x03ff +#define M12BITS 0x0fff +#define M13BITS 0x1fff +#define M16BITS 0xffff +#define M17BITS 0x1ffff + +#define MALIDP_H_FRONTPORCH(x) (((x) & M12BITS) << 0) +#define MALIDP_H_BACKPORCH(x) (((x) & M10BITS) << 16) +#define MALIDP_V_FRONTPORCH(x) (((x) & M12BITS) << 0) +#define MALIDP_V_BACKPORCH(x) (((x) & M8BITS) << 16) +#define MALIDP_H_SYNCWIDTH(x) (((x) & M10BITS) << 0) +#define MALIDP_V_SYNCWIDTH(x) (((x) & M8BITS) << 16) +#define MALIDP_H_ACTIVE(x) (((x) & M13BITS) << 0) +#define MALIDP_V_ACTIVE(x) (((x) & M13BITS) << 16) + +#define MALIDP_CMP_V_SIZE(x) (((x) & M13BITS) << 16) +#define MALIDP_CMP_H_SIZE(x) (((x) & M13BITS) << 0) + +#define MALIDP_IN_V_SIZE(x) (((x) & M13BITS) << 16) +#define MALIDP_IN_H_SIZE(x) (((x) & M13BITS) << 0) + +#define MALIDP_DC_CM_CONTROL(x) ((x) & M1BITS) << 16, 1 << 16 +#define MALIDP_DC_STATUS_GET_CM(reg) (((reg) >> 16) & M1BITS) + +#define MALIDP_FORMAT_ARGB8888 0x08 +#define MALIDP_DEFAULT_BG_R 0x0 +#define MALIDP_DEFAULT_BG_G 0x0 +#define MALIDP_DEFAULT_BG_B 0x0 + +#define MALIDP_PRODUCT_ID(core_id) ((u32)(core_id) >> 16) + +#define MALIDP500 0x500 + +DECLARE_GLOBAL_DATA_PTR; + +struct malidp_priv { + phys_addr_t base_addr; + phys_addr_t dc_status_addr; + phys_addr_t dc_control_addr; + phys_addr_t cval_addr; + struct udevice *display; /* display device attached */ + struct clk aclk; + struct clk pxlclk; + u16 modeset_regs_offset; + u8 config_bit_shift; + u8 clear_irq; /* offset for IRQ clear register */ +}; + +static const struct video_ops malidp_ops = { +}; + +static int malidp_get_hwid(phys_addr_t base_addr) +{ + int hwid; + + /* + * reading from the old CORE_ID offset will always + * return 0x5000000 on DP500 + */ + hwid = readl(base_addr + MALIDP_CORE_ID); + if (MALIDP_PRODUCT_ID(hwid) == MALIDP500) + return hwid; + /* otherwise try the other gen CORE_ID offset */ + hwid = readl(base_addr + MALIDP_DC_STATUS + MALIDP_CORE_ID); + + return hwid; +} + +/* + * wait for config mode bit setup to be acted upon by the hardware + */ +static int malidp_wait_configdone(struct malidp_priv *malidp) +{ + u32 status, tries = 300; + + while (tries--) { + status = readl(malidp->dc_status_addr); + if ((status >> malidp->config_bit_shift) & 1) + break; + udelay(500); + } + + if (!tries) + return -ETIMEDOUT; + + return 0; +} + +/* + * signal the hardware to enter configuration mode + */ +static int malidp_enter_config(struct malidp_priv *malidp) +{ + setbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift); + return malidp_wait_configdone(malidp); +} + +/* + * signal the hardware to exit configuration mode + */ +static int malidp_leave_config(struct malidp_priv *malidp) +{ + clrbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift); + return malidp_wait_configdone(malidp); +} + +static void malidp_setup_timings(struct malidp_priv *malidp, + struct display_timing *timings) +{ + u32 val = MALIDP_H_SYNCWIDTH(timings->hsync_len.typ) | + MALIDP_V_SYNCWIDTH(timings->vsync_len.typ); + writel(val, malidp->base_addr + malidp->modeset_regs_offset + + MALIDP_SYNC_CONTROL); + val = MALIDP_H_BACKPORCH(timings->hback_porch.typ) | + MALIDP_H_FRONTPORCH(timings->hfront_porch.typ); + writel(val, malidp->base_addr + malidp->modeset_regs_offset + + MALIDP_H_INTERVALS); + val = MALIDP_V_BACKPORCH(timings->vback_porch.typ) | + MALIDP_V_FRONTPORCH(timings->vfront_porch.typ); + writel(val, malidp->base_addr + malidp->modeset_regs_offset + + MALIDP_V_INTERVALS); + val = MALIDP_H_ACTIVE(timings->hactive.typ) | + MALIDP_V_ACTIVE(timings->vactive.typ); + writel(val, malidp->base_addr + malidp->modeset_regs_offset + + MALIDP_HV_ACTIVESIZE); + /* default output bit-depth per colour is 8 bits */ + writel(0x080808, malidp->base_addr + malidp->modeset_regs_offset + + MALIDP_OUTPUT_DEPTH); +} + +static int malidp_setup_mode(struct malidp_priv *malidp, + struct display_timing *timings) +{ + int err; + + if (clk_set_rate(&malidp->pxlclk, timings->pixelclock.typ) == 0) + return -EIO; + + malidp_setup_timings(malidp, timings); + + err = display_enable(malidp->display, 8, timings); + if (err) + printf("display_enable failed with %d\n", err); + + return err; +} + +static void malidp_setup_layer(struct malidp_priv *malidp, + struct display_timing *timings, + u32 layer_offset, phys_addr_t fb_addr) +{ + u32 val; + + /* setup the base layer's pixel format to A8R8G8B8 */ + writel(MALIDP_FORMAT_ARGB8888, malidp->base_addr + layer_offset + + MALIDP_LAYER_FORMAT); + /* setup layer composition size */ + val = MALIDP_CMP_V_SIZE(timings->vactive.typ) | + MALIDP_CMP_H_SIZE(timings->hactive.typ); + writel(val, malidp->base_addr + layer_offset + + MALIDP_LAYER_CMP_SIZE); + /* setup layer input size */ + val = MALIDP_IN_V_SIZE(timings->vactive.typ) | + MALIDP_IN_H_SIZE(timings->hactive.typ); + writel(val, malidp->base_addr + layer_offset + MALIDP_LAYER_IN_SIZE); + /* setup layer stride in bytes */ + writel(timings->hactive.typ << 2, malidp->base_addr + layer_offset + + MALIDP_LAYER_STRIDE); + /* set framebuffer address */ + writel(lower_32_bits(fb_addr), malidp->base_addr + layer_offset + + MALIDP_LAYER_PTR_LOW); + writel(upper_32_bits(fb_addr), malidp->base_addr + layer_offset + + MALIDP_LAYER_PTR_HIGH); + /* enable layer */ + setbits_le32(malidp->base_addr + layer_offset + + MALIDP_LAYER_CONTROL, 1); +} + +static void malidp_set_configvalid(struct malidp_priv *malidp) +{ + setbits_le32(malidp->cval_addr, 1); +} + +static int malidp_update_timings_from_edid(struct udevice *dev, + struct display_timing *timings) +{ +#ifdef CONFIG_DISPLAY + struct malidp_priv *priv = dev_get_priv(dev); + struct udevice *disp_dev; + int err; + + err = uclass_first_device(UCLASS_DISPLAY, &disp_dev); + if (err) + return err; + + priv->display = disp_dev; + + err = display_read_timing(disp_dev, timings); + if (err) + return err; + +#endif + return 0; +} + +static int malidp_probe(struct udevice *dev) +{ + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); + ofnode framebuffer = ofnode_find_subnode(dev_ofnode(dev), "framebuffer"); + struct malidp_priv *priv = dev_get_priv(dev); + struct display_timing timings; + phys_addr_t fb_base, fb_size; + const char *format; + u32 value; + int err; + + if (!ofnode_valid(framebuffer)) + return -EINVAL; + + err = clk_get_by_name(dev, "pxlclk", &priv->pxlclk); + if (err) { + dev_err(dev, "failed to get pixel clock\n"); + return err; + } + err = clk_get_by_name(dev, "aclk", &priv->aclk); + if (err) { + dev_err(dev, "failed to get AXI clock\n"); + goto fail_aclk; + } + + err = ofnode_decode_display_timing(dev_ofnode(dev), 1, &timings); + if (err) { + dev_err(dev, "failed to get any display timings\n"); + goto fail_timings; + } + + err = malidp_update_timings_from_edid(dev, &timings); + if (err) { + printf("malidp_update_timings_from_edid failed: %d\n", err); + goto fail_timings; + } + + fb_base = ofnode_get_addr_size(framebuffer, "reg", &fb_size); + if (fb_base != FDT_ADDR_T_NONE) { + uc_plat->base = fb_base; + uc_plat->size = fb_size; + } else { + printf("cannot get address size for framebuffer\n"); + } + + err = ofnode_read_u32(framebuffer, "width", &value); + if (err) + goto fail_timings; + uc_priv->xsize = (ushort)value; + + err = ofnode_read_u32(framebuffer, "height", &value); + if (err) + goto fail_timings; + uc_priv->ysize = (ushort)value; + + format = ofnode_read_string(framebuffer, "format"); + if (!format) { + err = -EINVAL; + goto fail_timings; + } else if (!strncmp(format, "a8r8g8b8", 8)) { + uc_priv->bpix = VIDEO_BPP32; + } + + uc_priv->rot = 0; + priv->base_addr = (phys_addr_t)dev_read_addr(dev); + + clk_enable(&priv->pxlclk); + clk_enable(&priv->aclk); + + value = malidp_get_hwid(priv->base_addr); + printf("Display: Arm Mali DP%3x r%dp%d\n", MALIDP_PRODUCT_ID(value), + (value >> 12) & 0xf, (value >> 8) & 0xf); + + if (MALIDP_PRODUCT_ID(value) == MALIDP500) { + /* DP500 is special */ + priv->modeset_regs_offset = 0x28; + priv->dc_status_addr = priv->base_addr; + priv->dc_control_addr = priv->base_addr + 0xc; + priv->cval_addr = priv->base_addr + 0xf00; + priv->config_bit_shift = 17; + priv->clear_irq = 0; + } else { + priv->modeset_regs_offset = 0x30; + priv->dc_status_addr = priv->base_addr + MALIDP_DC_STATUS; + priv->dc_control_addr = priv->base_addr + MALIDP_DC_CONTROL; + priv->cval_addr = priv->base_addr + MALIDP_DC_CFG_VALID; + priv->config_bit_shift = 16; + priv->clear_irq = MALIDP_REG_CLEARIRQ; + } + + /* enter config mode */ + err = malidp_enter_config(priv); + if (err) + return err; + + /* disable interrupts */ + writel(0, priv->dc_status_addr + MALIDP_REG_MASKIRQ); + writel(0xffffffff, priv->dc_status_addr + priv->clear_irq); + + err = malidp_setup_mode(priv, &timings); + if (err) + goto fail_timings; + + malidp_setup_layer(priv, &timings, MALIDP_LAYER_LV1, + (phys_addr_t)uc_plat->base); + + err = malidp_leave_config(priv); + if (err) + goto fail_timings; + + malidp_set_configvalid(priv); + + return 0; + +fail_timings: + clk_free(&priv->aclk); +fail_aclk: + clk_free(&priv->pxlclk); + + return err; +} + +static int malidp_bind(struct udevice *dev) +{ + struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); + + /* choose max possible size: 2K x 2K, XRGB888 framebuffer */ + uc_plat->size = 4 * 2048 * 2048; + + return 0; +} + +static const struct udevice_id malidp_ids[] = { + { .compatible = "arm,mali-dp500" }, + { .compatible = "arm,mali-dp550" }, + { .compatible = "arm,mali-dp650" }, + { } +}; + +U_BOOT_DRIVER(mali_dp) = { + .name = "mali_dp", + .id = UCLASS_VIDEO, + .of_match = malidp_ids, + .bind = malidp_bind, + .probe = malidp_probe, + .priv_auto_alloc_size = sizeof(struct malidp_priv), + .ops = &malidp_ops, +}; -- cgit v1.2.3