From a6ea875b0017fac1228f935f916800cfd320aa14 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 14 Apr 2015 21:03:21 -0600 Subject: [PATCH] fdt: Add binding decode function for display-timings This is useful for display parameters. Add a simple decode function to read from this device tree node. Signed-off-by: Simon Glass Signed-off-by: Tom Warren --- .../video/display-timing.txt | 110 ++++++++++++++++++ include/fdtdec.h | 77 ++++++++++++ lib/fdtdec.c | 92 +++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 doc/device-tree-bindings/video/display-timing.txt diff --git a/doc/device-tree-bindings/video/display-timing.txt b/doc/device-tree-bindings/video/display-timing.txt new file mode 100644 index 0000000000..e1d4a0b596 --- /dev/null +++ b/doc/device-tree-bindings/video/display-timing.txt @@ -0,0 +1,110 @@ +display-timing bindings +======================= + +display-timings node +-------------------- + +required properties: + - none + +optional properties: + - native-mode: The native mode for the display, in case multiple modes are + provided. When omitted, assume the first node is the native. + +timing subnode +-------------- + +required properties: + - hactive, vactive: display resolution + - hfront-porch, hback-porch, hsync-len: horizontal display timing parameters + in pixels + vfront-porch, vback-porch, vsync-len: vertical display timing parameters in + lines + - clock-frequency: display clock in Hz + +optional properties: + - hsync-active: hsync pulse is active low/high/ignored + - vsync-active: vsync pulse is active low/high/ignored + - de-active: data-enable pulse is active low/high/ignored + - pixelclk-active: with + - active high = drive pixel data on rising edge/ + sample data on falling edge + - active low = drive pixel data on falling edge/ + sample data on rising edge + - ignored = ignored + - interlaced (bool): boolean to enable interlaced mode + - doublescan (bool): boolean to enable doublescan mode + - doubleclk (bool): boolean to enable doubleclock mode + +All the optional properties that are not bool follow the following logic: + <1>: high active + <0>: low active + omitted: not used on hardware + +There are different ways of describing the capabilities of a display. The +devicetree representation corresponds to the one commonly found in datasheets +for displays. If a display supports multiple signal timings, the native-mode +can be specified. + +The parameters are defined as: + + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vback_porch | | | + | | ↓ | | | + +----------#######################################----------+-------+ + | # ↑ # | | + | # | # | | + | hback # | # hfront | hsync | + | porch # | hactive # porch | len | + |<-------->#<-------+--------------------------->#<-------->|<----->| + | # | # | | + | # |vactive # | | + | # | # | | + | # ↓ # | | + +----------#######################################----------+-------+ + | | ↑ | | | + | | |vfront_porch | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vsync_len | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + +Example: + + display-timings { + native-mode = <&timing0>; + timing0: 1080p24 { + /* 1920x1080p24 */ + clock-frequency = <52000000>; + hactive = <1920>; + vactive = <1080>; + hfront-porch = <25>; + hback-porch = <25>; + hsync-len = <25>; + vback-porch = <2>; + vfront-porch = <2>; + vsync-len = <2>; + hsync-active = <1>; + }; + }; + +Every required property also supports the use of ranges, so the commonly used +datasheet description with minimum, typical and maximum values can be used. + +Example: + + timing1: timing { + /* 1920x1080p24 */ + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hsync-len = <0 44 60>; + hfront-porch = <80 88 95>; + hback-porch = <100 148 160>; + vfront-porch = <0 4 6>; + vback-porch = <0 36 50>; + vsync-len = <0 5 6>; + }; diff --git a/include/fdtdec.h b/include/fdtdec.h index f11475b5fd..f304199e1d 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -802,6 +802,83 @@ int fdtdec_decode_memory_region(const void *blob, int node, const char *mem_type, const char *suffix, fdt_addr_t *basep, fdt_size_t *sizep); +/* Display timings from linux include/video/display_timing.h */ +enum display_flags { + DISPLAY_FLAGS_HSYNC_LOW = 1 << 0, + DISPLAY_FLAGS_HSYNC_HIGH = 1 << 1, + DISPLAY_FLAGS_VSYNC_LOW = 1 << 2, + DISPLAY_FLAGS_VSYNC_HIGH = 1 << 3, + + /* data enable flag */ + DISPLAY_FLAGS_DE_LOW = 1 << 4, + DISPLAY_FLAGS_DE_HIGH = 1 << 5, + /* drive data on pos. edge */ + DISPLAY_FLAGS_PIXDATA_POSEDGE = 1 << 6, + /* drive data on neg. edge */ + DISPLAY_FLAGS_PIXDATA_NEGEDGE = 1 << 7, + DISPLAY_FLAGS_INTERLACED = 1 << 8, + DISPLAY_FLAGS_DOUBLESCAN = 1 << 9, + DISPLAY_FLAGS_DOUBLECLK = 1 << 10, +}; + +/* + * A single signal can be specified via a range of minimal and maximal values + * with a typical value, that lies somewhere inbetween. + */ +struct timing_entry { + u32 min; + u32 typ; + u32 max; +}; + +/* + * Single "mode" entry. This describes one set of signal timings a display can + * have in one setting. This struct can later be converted to struct videomode + * (see include/video/videomode.h). As each timing_entry can be defined as a + * range, one struct display_timing may become multiple struct videomodes. + * + * Example: hsync active high, vsync active low + * + * Active Video + * Video ______________________XXXXXXXXXXXXXXXXXXXXXX_____________________ + * |<- sync ->|<- back ->|<----- active ----->|<- front ->|<- sync.. + * | | porch | | porch | + * + * HSync _|¯¯¯¯¯¯¯¯¯¯|___________________________________________|¯¯¯¯¯¯¯¯¯ + * + * VSync ¯|__________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_________ + */ +struct display_timing { + struct timing_entry pixelclock; + + struct timing_entry hactive; /* hor. active video */ + struct timing_entry hfront_porch; /* hor. front porch */ + struct timing_entry hback_porch; /* hor. back porch */ + struct timing_entry hsync_len; /* hor. sync len */ + + struct timing_entry vactive; /* ver. active video */ + struct timing_entry vfront_porch; /* ver. front porch */ + struct timing_entry vback_porch; /* ver. back porch */ + struct timing_entry vsync_len; /* ver. sync len */ + + enum display_flags flags; /* display flags */ +}; + +/** + * fdtdec_decode_display_timing() - decode display timings + * + * Decode display timings from the supplied 'display-timings' node. + * See doc/device-tree-bindings/video/display-timing.txt for binding + * information. + * + * @param blob FDT blob + * @param node 'display-timing' node containing the timing subnodes + * @param index Index number to read (0=first timing subnode) + * @param config Place to put timings + * @return 0 if OK, -FDT_ERR_NOTFOUND if not found + */ +int fdtdec_decode_display_timing(const void *blob, int node, int index, + struct display_timing *config); /** * Set up the device tree ready for use */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index b76d9cad83..1007fa0372 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1037,6 +1037,98 @@ int fdtdec_decode_memory_region(const void *blob, int config_node, return 0; } +static int decode_timing_property(const void *blob, int node, const char *name, + struct timing_entry *result) +{ + int length, ret = 0; + const u32 *prop; + + prop = fdt_getprop(blob, node, name, &length); + if (!prop) { + debug("%s: could not find property %s\n", + fdt_get_name(blob, node, NULL), name); + return length; + } + + if (length == sizeof(u32)) { + result->typ = fdtdec_get_int(blob, node, name, 0); + result->min = result->typ; + result->max = result->typ; + } else { + ret = fdtdec_get_int_array(blob, node, name, &result->min, 3); + } + + return ret; +} + +int fdtdec_decode_display_timing(const void *blob, int parent, int index, + struct display_timing *dt) +{ + int i, node, timings_node; + u32 val = 0; + int ret = 0; + + timings_node = fdt_subnode_offset(blob, parent, "display-timings"); + if (timings_node < 0) + return timings_node; + + for (i = 0, node = fdt_first_subnode(blob, timings_node); + node > 0 && i != index; + node = fdt_next_subnode(blob, node)) + i++; + + if (node < 0) + return node; + + memset(dt, 0, sizeof(*dt)); + + ret |= decode_timing_property(blob, node, "hback-porch", + &dt->hback_porch); + ret |= decode_timing_property(blob, node, "hfront-porch", + &dt->hfront_porch); + ret |= decode_timing_property(blob, node, "hactive", &dt->hactive); + ret |= decode_timing_property(blob, node, "hsync-len", &dt->hsync_len); + ret |= decode_timing_property(blob, node, "vback-porch", + &dt->vback_porch); + ret |= decode_timing_property(blob, node, "vfront-porch", + &dt->vfront_porch); + ret |= decode_timing_property(blob, node, "vactive", &dt->vactive); + ret |= decode_timing_property(blob, node, "vsync-len", &dt->vsync_len); + ret |= decode_timing_property(blob, node, "clock-frequency", + &dt->pixelclock); + + dt->flags = 0; + val = fdtdec_get_int(blob, node, "vsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; + } + val = fdtdec_get_int(blob, node, "hsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; + } + val = fdtdec_get_int(blob, node, "de-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + } + val = fdtdec_get_int(blob, node, "pixelclk-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + } + + if (fdtdec_get_bool(blob, node, "interlaced")) + dt->flags |= DISPLAY_FLAGS_INTERLACED; + if (fdtdec_get_bool(blob, node, "doublescan")) + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (fdtdec_get_bool(blob, node, "doubleclk")) + dt->flags |= DISPLAY_FLAGS_DOUBLECLK; + + return 0; +} + int fdtdec_setup(void) { #ifdef CONFIG_OF_CONTROL -- 2.39.2