Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 358102
b: refs/heads/master
c: cc3f414
h: refs/heads/master
v: v3
  • Loading branch information
Steffen Trumtrar committed Jan 24, 2013
1 parent 41dfc00 commit caed878
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8714c0cecfc28f7ce73a520be4831f09743c4fd7
refs/heads/master: cc3f414cf2e404130584b63d373161ba6fd24bc2
109 changes: 109 additions & 0 deletions trunk/Documentation/devicetree/bindings/video/display-timing.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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

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>;
};
15 changes: 15 additions & 0 deletions trunk/drivers/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ config DISPLAY_TIMING
config VIDEOMODE
bool

config OF_DISPLAY_TIMING
bool "Enable device tree display timing support"
depends on OF
select DISPLAY_TIMING
help
helper to parse display timings from the devicetree

config OF_VIDEOMODE
bool "Enable device tree videomode support"
depends on OF
select VIDEOMODE
select OF_DISPLAY_TIMING
help
helper to get videomodes from the devicetree

menuconfig FB
tristate "Support for frame buffer devices"
---help---
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/video/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,6 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o
#video output switch sysfs driver
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
obj-$(CONFIG_VIDEOMODE) += videomode.o
obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
239 changes: 239 additions & 0 deletions trunk/drivers/video/of_display_timing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* OF helpers for parsing display timings
*
* Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
*
* based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
*
* This file is released under the GPLv2
*/
#include <linux/export.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>

/**
* parse_timing_property - parse timing_entry from device_node
* @np: device_node with the property
* @name: name of the property
* @result: will be set to the return value
*
* DESCRIPTION:
* Every display_timing can be specified with either just the typical value or
* a range consisting of min/typ/max. This function helps handling this
**/
static int parse_timing_property(struct device_node *np, const char *name,
struct timing_entry *result)
{
struct property *prop;
int length, cells, ret;

prop = of_find_property(np, name, &length);
if (!prop) {
pr_err("%s: could not find property %s\n",
of_node_full_name(np), name);
return -EINVAL;
}

cells = length / sizeof(u32);
if (cells == 1) {
ret = of_property_read_u32(np, name, &result->typ);
result->min = result->typ;
result->max = result->typ;
} else if (cells == 3) {
ret = of_property_read_u32_array(np, name, &result->min, cells);
} else {
pr_err("%s: illegal timing specification in %s\n",
of_node_full_name(np), name);
return -EINVAL;
}

return ret;
}

/**
* of_get_display_timing - parse display_timing entry from device_node
* @np: device_node with the properties
**/
static struct display_timing *of_get_display_timing(struct device_node *np)
{
struct display_timing *dt;
u32 val = 0;
int ret = 0;

dt = kzalloc(sizeof(*dt), GFP_KERNEL);
if (!dt) {
pr_err("%s: could not allocate display_timing struct\n",
of_node_full_name(np));
return NULL;
}

ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
ret |= parse_timing_property(np, "hactive", &dt->hactive);
ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len);
ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch);
ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch);
ret |= parse_timing_property(np, "vactive", &dt->vactive);
ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len);
ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock);

dt->dmt_flags = 0;
dt->data_flags = 0;
if (!of_property_read_u32(np, "vsync-active", &val))
dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH :
VESA_DMT_VSYNC_LOW;
if (!of_property_read_u32(np, "hsync-active", &val))
dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH :
VESA_DMT_HSYNC_LOW;
if (!of_property_read_u32(np, "de-active", &val))
dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
DISPLAY_FLAGS_DE_LOW;
if (!of_property_read_u32(np, "pixelclk-active", &val))
dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
DISPLAY_FLAGS_PIXDATA_NEGEDGE;

if (of_property_read_bool(np, "interlaced"))
dt->data_flags |= DISPLAY_FLAGS_INTERLACED;
if (of_property_read_bool(np, "doublescan"))
dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;

if (ret) {
pr_err("%s: error reading timing properties\n",
of_node_full_name(np));
kfree(dt);
return NULL;
}

return dt;
}

/**
* of_get_display_timings - parse all display_timing entries from a device_node
* @np: device_node with the subnodes
**/
struct display_timings *of_get_display_timings(struct device_node *np)
{
struct device_node *timings_np;
struct device_node *entry;
struct device_node *native_mode;
struct display_timings *disp;

if (!np) {
pr_err("%s: no devicenode given\n", of_node_full_name(np));
return NULL;
}

timings_np = of_find_node_by_name(np, "display-timings");
if (!timings_np) {
pr_err("%s: could not find display-timings node\n",
of_node_full_name(np));
return NULL;
}

disp = kzalloc(sizeof(*disp), GFP_KERNEL);
if (!disp) {
pr_err("%s: could not allocate struct disp'\n",
of_node_full_name(np));
goto dispfail;
}

entry = of_parse_phandle(timings_np, "native-mode", 0);
/* assume first child as native mode if none provided */
if (!entry)
entry = of_get_next_child(np, NULL);
/* if there is no child, it is useless to go on */
if (!entry) {
pr_err("%s: no timing specifications given\n",
of_node_full_name(np));
goto entryfail;
}

pr_debug("%s: using %s as default timing\n",
of_node_full_name(np), entry->name);

native_mode = entry;

disp->num_timings = of_get_child_count(timings_np);
if (disp->num_timings == 0) {
/* should never happen, as entry was already found above */
pr_err("%s: no timings specified\n", of_node_full_name(np));
goto entryfail;
}

disp->timings = kzalloc(sizeof(struct display_timing *) *
disp->num_timings, GFP_KERNEL);
if (!disp->timings) {
pr_err("%s: could not allocate timings array\n",
of_node_full_name(np));
goto entryfail;
}

disp->num_timings = 0;
disp->native_mode = 0;

for_each_child_of_node(timings_np, entry) {
struct display_timing *dt;

dt = of_get_display_timing(entry);
if (!dt) {
/*
* to not encourage wrong devicetrees, fail in case of
* an error
*/
pr_err("%s: error in timing %d\n",
of_node_full_name(np), disp->num_timings + 1);
goto timingfail;
}

if (native_mode == entry)
disp->native_mode = disp->num_timings;

disp->timings[disp->num_timings] = dt;
disp->num_timings++;
}
of_node_put(timings_np);
/*
* native_mode points to the device_node returned by of_parse_phandle
* therefore call of_node_put on it
*/
of_node_put(native_mode);

pr_debug("%s: got %d timings. Using timing #%d as default\n",
of_node_full_name(np), disp->num_timings,
disp->native_mode + 1);

return disp;

timingfail:
if (native_mode)
of_node_put(native_mode);
display_timings_release(disp);
entryfail:
kfree(disp);
dispfail:
of_node_put(timings_np);
return NULL;
}
EXPORT_SYMBOL_GPL(of_get_display_timings);

/**
* of_display_timings_exist - check if a display-timings node is provided
* @np: device_node with the timing
**/
int of_display_timings_exist(struct device_node *np)
{
struct device_node *timings_np;

if (!np)
return -EINVAL;

timings_np = of_parse_phandle(np, "display-timings", 0);
if (!timings_np)
return -EINVAL;

of_node_put(timings_np);
return 1;
}
EXPORT_SYMBOL_GPL(of_display_timings_exist);
Loading

0 comments on commit caed878

Please sign in to comment.