aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorSteffen Trumtrar <s.trumtrar@pengutronix.de>2012-10-04 09:32:52 -0400
committerSteffen Trumtrar <s.trumtrar@pengutronix.de>2013-01-24 03:03:48 -0500
commitcc3f414cf2e404130584b63d373161ba6fd24bc2 (patch)
tree08d61d026631c79a176f514fb1364b71011599ba /drivers/video
parent8714c0cecfc28f7ce73a520be4831f09743c4fd7 (diff)
video: add of helper for display timings/videomode
This adds support for reading display timings from DT into a struct display_timings. The of_display_timing implementation supports multiple subnodes. All children are read into an array, that can be queried. If no native mode is specified, the first subnode will be used. For cases where the graphics driver knows there can be only one mode description or where the driver only supports one mode, a helper function of_get_videomode is added, that gets a struct videomode from DT. Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> Acked-by: Stephen Warren <swarren@nvidia.com> Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de> Acked-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Philipp Zabel <p.zabel@pengutronix.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Afzal Mohammed <Afzal@ti.com> Tested-by: Rob Clark <robclark@gmail.com> Tested-by: Leela Krishna Amudala <leelakrishna.a@gmail.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig15
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/of_display_timing.c239
-rw-r--r--drivers/video/of_videomode.c54
4 files changed, 310 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 09a8f0d8a3d4..807c7fa689fa 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -39,6 +39,21 @@ config DISPLAY_TIMING
39config VIDEOMODE 39config VIDEOMODE
40 bool 40 bool
41 41
42config OF_DISPLAY_TIMING
43 bool "Enable device tree display timing support"
44 depends on OF
45 select DISPLAY_TIMING
46 help
47 helper to parse display timings from the devicetree
48
49config OF_VIDEOMODE
50 bool "Enable device tree videomode support"
51 depends on OF
52 select VIDEOMODE
53 select OF_DISPLAY_TIMING
54 help
55 helper to get videomodes from the devicetree
56
42menuconfig FB 57menuconfig FB
43 tristate "Support for frame buffer devices" 58 tristate "Support for frame buffer devices"
44 ---help--- 59 ---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e0dd8202365f..f592f3b32ec7 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -169,4 +169,6 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o
169#video output switch sysfs driver 169#video output switch sysfs driver
170obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o 170obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
171obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o 171obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
172obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
172obj-$(CONFIG_VIDEOMODE) += videomode.o 173obj-$(CONFIG_VIDEOMODE) += videomode.o
174obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
new file mode 100644
index 000000000000..13ecd9897010
--- /dev/null
+++ b/drivers/video/of_display_timing.c
@@ -0,0 +1,239 @@
1/*
2 * OF helpers for parsing display timings
3 *
4 * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
5 *
6 * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
7 *
8 * This file is released under the GPLv2
9 */
10#include <linux/export.h>
11#include <linux/of.h>
12#include <linux/slab.h>
13#include <video/display_timing.h>
14#include <video/of_display_timing.h>
15
16/**
17 * parse_timing_property - parse timing_entry from device_node
18 * @np: device_node with the property
19 * @name: name of the property
20 * @result: will be set to the return value
21 *
22 * DESCRIPTION:
23 * Every display_timing can be specified with either just the typical value or
24 * a range consisting of min/typ/max. This function helps handling this
25 **/
26static int parse_timing_property(struct device_node *np, const char *name,
27 struct timing_entry *result)
28{
29 struct property *prop;
30 int length, cells, ret;
31
32 prop = of_find_property(np, name, &length);
33 if (!prop) {
34 pr_err("%s: could not find property %s\n",
35 of_node_full_name(np), name);
36 return -EINVAL;
37 }
38
39 cells = length / sizeof(u32);
40 if (cells == 1) {
41 ret = of_property_read_u32(np, name, &result->typ);
42 result->min = result->typ;
43 result->max = result->typ;
44 } else if (cells == 3) {
45 ret = of_property_read_u32_array(np, name, &result->min, cells);
46 } else {
47 pr_err("%s: illegal timing specification in %s\n",
48 of_node_full_name(np), name);
49 return -EINVAL;
50 }
51
52 return ret;
53}
54
55/**
56 * of_get_display_timing - parse display_timing entry from device_node
57 * @np: device_node with the properties
58 **/
59static struct display_timing *of_get_display_timing(struct device_node *np)
60{
61 struct display_timing *dt;
62 u32 val = 0;
63 int ret = 0;
64
65 dt = kzalloc(sizeof(*dt), GFP_KERNEL);
66 if (!dt) {
67 pr_err("%s: could not allocate display_timing struct\n",
68 of_node_full_name(np));
69 return NULL;
70 }
71
72 ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
73 ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
74 ret |= parse_timing_property(np, "hactive", &dt->hactive);
75 ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len);
76 ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch);
77 ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch);
78 ret |= parse_timing_property(np, "vactive", &dt->vactive);
79 ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len);
80 ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock);
81
82 dt->dmt_flags = 0;
83 dt->data_flags = 0;
84 if (!of_property_read_u32(np, "vsync-active", &val))
85 dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH :
86 VESA_DMT_VSYNC_LOW;
87 if (!of_property_read_u32(np, "hsync-active", &val))
88 dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH :
89 VESA_DMT_HSYNC_LOW;
90 if (!of_property_read_u32(np, "de-active", &val))
91 dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
92 DISPLAY_FLAGS_DE_LOW;
93 if (!of_property_read_u32(np, "pixelclk-active", &val))
94 dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
95 DISPLAY_FLAGS_PIXDATA_NEGEDGE;
96
97 if (of_property_read_bool(np, "interlaced"))
98 dt->data_flags |= DISPLAY_FLAGS_INTERLACED;
99 if (of_property_read_bool(np, "doublescan"))
100 dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;
101
102 if (ret) {
103 pr_err("%s: error reading timing properties\n",
104 of_node_full_name(np));
105 kfree(dt);
106 return NULL;
107 }
108
109 return dt;
110}
111
112/**
113 * of_get_display_timings - parse all display_timing entries from a device_node
114 * @np: device_node with the subnodes
115 **/
116struct display_timings *of_get_display_timings(struct device_node *np)
117{
118 struct device_node *timings_np;
119 struct device_node *entry;
120 struct device_node *native_mode;
121 struct display_timings *disp;
122
123 if (!np) {
124 pr_err("%s: no devicenode given\n", of_node_full_name(np));
125 return NULL;
126 }
127
128 timings_np = of_find_node_by_name(np, "display-timings");
129 if (!timings_np) {
130 pr_err("%s: could not find display-timings node\n",
131 of_node_full_name(np));
132 return NULL;
133 }
134
135 disp = kzalloc(sizeof(*disp), GFP_KERNEL);
136 if (!disp) {
137 pr_err("%s: could not allocate struct disp'\n",
138 of_node_full_name(np));
139 goto dispfail;
140 }
141
142 entry = of_parse_phandle(timings_np, "native-mode", 0);
143 /* assume first child as native mode if none provided */
144 if (!entry)
145 entry = of_get_next_child(np, NULL);
146 /* if there is no child, it is useless to go on */
147 if (!entry) {
148 pr_err("%s: no timing specifications given\n",
149 of_node_full_name(np));
150 goto entryfail;
151 }
152
153 pr_debug("%s: using %s as default timing\n",
154 of_node_full_name(np), entry->name);
155
156 native_mode = entry;
157
158 disp->num_timings = of_get_child_count(timings_np);
159 if (disp->num_timings == 0) {
160 /* should never happen, as entry was already found above */
161 pr_err("%s: no timings specified\n", of_node_full_name(np));
162 goto entryfail;
163 }
164
165 disp->timings = kzalloc(sizeof(struct display_timing *) *
166 disp->num_timings, GFP_KERNEL);
167 if (!disp->timings) {
168 pr_err("%s: could not allocate timings array\n",
169 of_node_full_name(np));
170 goto entryfail;
171 }
172
173 disp->num_timings = 0;
174 disp->native_mode = 0;
175
176 for_each_child_of_node(timings_np, entry) {
177 struct display_timing *dt;
178
179 dt = of_get_display_timing(entry);
180 if (!dt) {
181 /*
182 * to not encourage wrong devicetrees, fail in case of
183 * an error
184 */
185 pr_err("%s: error in timing %d\n",
186 of_node_full_name(np), disp->num_timings + 1);
187 goto timingfail;
188 }
189
190 if (native_mode == entry)
191 disp->native_mode = disp->num_timings;
192
193 disp->timings[disp->num_timings] = dt;
194 disp->num_timings++;
195 }
196 of_node_put(timings_np);
197 /*
198 * native_mode points to the device_node returned by of_parse_phandle
199 * therefore call of_node_put on it
200 */
201 of_node_put(native_mode);
202
203 pr_debug("%s: got %d timings. Using timing #%d as default\n",
204 of_node_full_name(np), disp->num_timings,
205 disp->native_mode + 1);
206
207 return disp;
208
209timingfail:
210 if (native_mode)
211 of_node_put(native_mode);
212 display_timings_release(disp);
213entryfail:
214 kfree(disp);
215dispfail:
216 of_node_put(timings_np);
217 return NULL;
218}
219EXPORT_SYMBOL_GPL(of_get_display_timings);
220
221/**
222 * of_display_timings_exist - check if a display-timings node is provided
223 * @np: device_node with the timing
224 **/
225int of_display_timings_exist(struct device_node *np)
226{
227 struct device_node *timings_np;
228
229 if (!np)
230 return -EINVAL;
231
232 timings_np = of_parse_phandle(np, "display-timings", 0);
233 if (!timings_np)
234 return -EINVAL;
235
236 of_node_put(timings_np);
237 return 1;
238}
239EXPORT_SYMBOL_GPL(of_display_timings_exist);
diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c
new file mode 100644
index 000000000000..5b8066cd397f
--- /dev/null
+++ b/drivers/video/of_videomode.c
@@ -0,0 +1,54 @@
1/*
2 * generic videomode helper
3 *
4 * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
5 *
6 * This file is released under the GPLv2
7 */
8#include <linux/errno.h>
9#include <linux/export.h>
10#include <linux/of.h>
11#include <video/display_timing.h>
12#include <video/of_display_timing.h>
13#include <video/of_videomode.h>
14#include <video/videomode.h>
15
16/**
17 * of_get_videomode - get the videomode #<index> from devicetree
18 * @np - devicenode with the display_timings
19 * @vm - set to return value
20 * @index - index into list of display_timings
21 * (Set this to OF_USE_NATIVE_MODE to use whatever mode is
22 * specified as native mode in the DT.)
23 *
24 * DESCRIPTION:
25 * Get a list of all display timings and put the one
26 * specified by index into *vm. This function should only be used, if
27 * only one videomode is to be retrieved. A driver that needs to work
28 * with multiple/all videomodes should work with
29 * of_get_display_timings instead.
30 **/
31int of_get_videomode(struct device_node *np, struct videomode *vm,
32 int index)
33{
34 struct display_timings *disp;
35 int ret;
36
37 disp = of_get_display_timings(np);
38 if (!disp) {
39 pr_err("%s: no timings specified\n", of_node_full_name(np));
40 return -EINVAL;
41 }
42
43 if (index == OF_USE_NATIVE_MODE)
44 index = disp->native_mode;
45
46 ret = videomode_from_timing(disp, vm, index);
47 if (ret)
48 return ret;
49
50 display_timings_release(disp);
51
52 return 0;
53}
54EXPORT_SYMBOL_GPL(of_get_videomode);