/src/barebox/drivers/video/of_display_timing.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | /* |
3 | | * OF helpers for parsing display timings |
4 | | * |
5 | | * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix |
6 | | * |
7 | | * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de> |
8 | | */ |
9 | | #include <common.h> |
10 | | #include <of.h> |
11 | | #include <fb.h> |
12 | | #include <malloc.h> |
13 | | |
14 | | void display_timings_release(struct display_timings *disp) |
15 | 0 | { |
16 | 0 | if (disp->modes) |
17 | 0 | free(disp->modes); |
18 | 0 | free(disp); |
19 | 0 | } |
20 | | EXPORT_SYMBOL_GPL(display_timings_release); |
21 | | |
22 | | /** |
23 | | * parse_timing_property - parse timing_entry from device_node |
24 | | * @np: device_node with the property |
25 | | * @name: name of the property |
26 | | * @result: will be set to the return value |
27 | | * |
28 | | * DESCRIPTION: |
29 | | * Every display_timing can be specified with either just the typical value or |
30 | | * a range consisting of min/typ/max. This function helps handling this |
31 | | **/ |
32 | | static int parse_timing_property(const struct device_node *np, const char *name, |
33 | | u32 *res) |
34 | 0 | { |
35 | 0 | struct property *prop; |
36 | 0 | int length, cells, ret; |
37 | |
|
38 | 0 | prop = of_find_property(np, name, &length); |
39 | 0 | if (!prop) { |
40 | 0 | pr_err("%pOF: could not find property %s\n", |
41 | 0 | np, name); |
42 | 0 | return -EINVAL; |
43 | 0 | } |
44 | | |
45 | 0 | cells = length / sizeof(u32); |
46 | 0 | if ((cells == 1) || (cells == 3)) { |
47 | 0 | ret = of_property_read_u32(np, name, res); |
48 | 0 | } else { |
49 | 0 | pr_err("%pOF: illegal timing specification in %s\n", |
50 | 0 | np, name); |
51 | 0 | return -EINVAL; |
52 | 0 | } |
53 | | |
54 | 0 | return ret; |
55 | 0 | } |
56 | | |
57 | | /** |
58 | | * of_parse_display_timing - parse display_timing entry from device_node |
59 | | * @np: device_node with the properties |
60 | | **/ |
61 | | static int of_parse_display_timing(const struct device_node *np, |
62 | | struct fb_videomode *mode) |
63 | 0 | { |
64 | 0 | u32 val = 0, pixelclock = 0; |
65 | 0 | int ret = 0; |
66 | |
|
67 | 0 | memset(mode, 0, sizeof(*mode)); |
68 | |
|
69 | 0 | ret |= parse_timing_property(np, "hback-porch", &mode->left_margin); |
70 | 0 | ret |= parse_timing_property(np, "hfront-porch", &mode->right_margin); |
71 | 0 | ret |= parse_timing_property(np, "hactive", &mode->xres); |
72 | 0 | ret |= parse_timing_property(np, "hsync-len", &mode->hsync_len); |
73 | 0 | ret |= parse_timing_property(np, "vback-porch", &mode->upper_margin); |
74 | 0 | ret |= parse_timing_property(np, "vfront-porch", &mode->lower_margin); |
75 | 0 | ret |= parse_timing_property(np, "vactive", &mode->yres); |
76 | 0 | ret |= parse_timing_property(np, "vsync-len", &mode->vsync_len); |
77 | 0 | ret |= parse_timing_property(np, "clock-frequency", &pixelclock); |
78 | |
|
79 | 0 | fb_videomode_set_pixclock_hz(mode, pixelclock); |
80 | |
|
81 | 0 | if (!of_property_read_u32(np, "vsync-active", &val)) |
82 | 0 | mode->sync |= val ? FB_SYNC_VERT_HIGH_ACT : 0; |
83 | 0 | if (!of_property_read_u32(np, "hsync-active", &val)) |
84 | 0 | mode->sync |= val ? FB_SYNC_HOR_HIGH_ACT : 0; |
85 | 0 | if (!of_property_read_u32(np, "de-active", &val)) |
86 | 0 | mode->display_flags |= val ? DISPLAY_FLAGS_DE_HIGH : |
87 | 0 | DISPLAY_FLAGS_DE_LOW; |
88 | 0 | if (!of_property_read_u32(np, "pixelclk-active", &val)) |
89 | 0 | mode->display_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : |
90 | 0 | DISPLAY_FLAGS_PIXDATA_NEGEDGE; |
91 | |
|
92 | 0 | if (ret) { |
93 | 0 | pr_err("%pOF: error reading timing properties\n", np); |
94 | 0 | return -EINVAL; |
95 | 0 | } |
96 | | |
97 | 0 | return 0; |
98 | 0 | } |
99 | | |
100 | | /** |
101 | | * of_get_display_timing - parse a display_timing entry |
102 | | * @np: device_node with the timing subnode |
103 | | * @name: name of the timing node |
104 | | * @mode: fb_videomode struct to fill |
105 | | **/ |
106 | | int of_get_display_timing(const struct device_node *np, const char *name, |
107 | | struct fb_videomode *mode) |
108 | 0 | { |
109 | 0 | struct device_node *timing_np; |
110 | |
|
111 | 0 | if (!np) |
112 | 0 | return -EINVAL; |
113 | | |
114 | 0 | timing_np = of_get_child_by_name(np, name); |
115 | 0 | if (!timing_np) |
116 | 0 | return -ENOENT; |
117 | | |
118 | 0 | return of_parse_display_timing(timing_np, mode); |
119 | 0 | } |
120 | | EXPORT_SYMBOL_GPL(of_get_display_timing); |
121 | | |
122 | | /** |
123 | | * of_get_display_timings - parse all display_timing entries from a device_node |
124 | | * @np: device_node with the subnodes |
125 | | **/ |
126 | | struct display_timings *of_get_display_timings(struct device_node *np) |
127 | 0 | { |
128 | 0 | struct device_node *timings_np; |
129 | 0 | struct device_node *entry; |
130 | 0 | struct device_node *native_mode; |
131 | 0 | struct display_timings *disp; |
132 | |
|
133 | 0 | if (!np) |
134 | 0 | return NULL; |
135 | | |
136 | 0 | timings_np = of_get_child_by_name(np, "display-timings"); |
137 | 0 | if (!timings_np) { |
138 | 0 | pr_debug("%pOF: could not find display-timings node\n", np); |
139 | 0 | return NULL; |
140 | 0 | } |
141 | | |
142 | 0 | disp = xzalloc(sizeof(*disp)); |
143 | |
|
144 | 0 | entry = of_parse_phandle(timings_np, "native-mode", 0); |
145 | | /* assume first child as native mode if none provided */ |
146 | 0 | if (!entry) |
147 | 0 | entry = of_get_next_available_child(np, NULL); |
148 | | /* if there is no child, it is useless to go on */ |
149 | 0 | if (!entry) { |
150 | 0 | pr_err("%pOF: no timing specifications given\n", np); |
151 | 0 | goto fail; |
152 | 0 | } |
153 | | |
154 | 0 | pr_debug("%pOF: using %s as default timing\n", |
155 | 0 | np, entry->name); |
156 | |
|
157 | 0 | native_mode = entry; |
158 | |
|
159 | 0 | disp->num_modes = of_get_child_count(timings_np); |
160 | 0 | if (disp->num_modes == 0) { |
161 | | /* should never happen, as entry was already found above */ |
162 | 0 | pr_err("%pOF: no timings specified\n", np); |
163 | 0 | goto fail; |
164 | 0 | } |
165 | | |
166 | 0 | disp->modes = xzalloc(sizeof(struct fb_videomode) * disp->num_modes); |
167 | |
|
168 | 0 | disp->num_modes = 0; |
169 | 0 | disp->native_mode = 0; |
170 | |
|
171 | 0 | for_each_child_of_node(timings_np, entry) { |
172 | 0 | struct fb_videomode *mode; |
173 | 0 | int r; |
174 | |
|
175 | 0 | mode = &disp->modes[disp->num_modes]; |
176 | |
|
177 | 0 | r = of_parse_display_timing(entry, mode); |
178 | 0 | if (r) { |
179 | | /* |
180 | | * to not encourage wrong devicetrees, fail in case of |
181 | | * an error |
182 | | */ |
183 | 0 | pr_err("%pOF: error in timing %d\n", |
184 | 0 | np, disp->num_modes + 1); |
185 | 0 | goto fail; |
186 | 0 | } |
187 | | |
188 | 0 | mode->name = xstrdup(entry->name); |
189 | |
|
190 | 0 | if (native_mode == entry) |
191 | 0 | disp->native_mode = disp->num_modes; |
192 | |
|
193 | 0 | disp->num_modes++; |
194 | 0 | } |
195 | | |
196 | 0 | pr_debug("%pOF: got %d timings. Using timing #%d as default\n", |
197 | 0 | np, disp->num_modes, |
198 | 0 | disp->native_mode + 1); |
199 | |
|
200 | 0 | return disp; |
201 | | |
202 | 0 | fail: |
203 | 0 | display_timings_release(disp); |
204 | |
|
205 | 0 | return NULL; |
206 | 0 | } |
207 | | EXPORT_SYMBOL_GPL(of_get_display_timings); |