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 <linux/export.h> |
10 | #include <linux/of.h> |
11 | #include <linux/slab.h> |
12 | #include <video/display_timing.h> |
13 | #include <video/of_display_timing.h> |
14 | |
15 | /** |
16 | * parse_timing_property - parse timing_entry from device_node |
17 | * @np: device_node with the property |
18 | * @name: name of the property |
19 | * @result: will be set to the return value |
20 | * |
21 | * DESCRIPTION: |
22 | * Every display_timing can be specified with either just the typical value or |
23 | * a range consisting of min/typ/max. This function helps handling this |
24 | **/ |
25 | static int parse_timing_property(const struct device_node *np, const char *name, |
26 | struct timing_entry *result) |
27 | { |
28 | struct property *prop; |
29 | int length, cells, ret; |
30 | |
31 | prop = of_find_property(np, name, lenp: &length); |
32 | if (!prop) { |
33 | pr_err("%pOF: could not find property %s\n" , np, name); |
34 | return -EINVAL; |
35 | } |
36 | |
37 | cells = length / sizeof(u32); |
38 | if (cells == 1) { |
39 | ret = of_property_read_u32(np, propname: name, out_value: &result->typ); |
40 | result->min = result->typ; |
41 | result->max = result->typ; |
42 | } else if (cells == 3) { |
43 | ret = of_property_read_u32_array(np, propname: name, out_values: &result->min, sz: cells); |
44 | } else { |
45 | pr_err("%pOF: illegal timing specification in %s\n" , np, name); |
46 | return -EINVAL; |
47 | } |
48 | |
49 | return ret; |
50 | } |
51 | |
52 | /** |
53 | * of_parse_display_timing - parse display_timing entry from device_node |
54 | * @np: device_node with the properties |
55 | * @dt: display_timing that contains the result. I may be partially written in case of errors |
56 | **/ |
57 | static int of_parse_display_timing(const struct device_node *np, |
58 | struct display_timing *dt) |
59 | { |
60 | u32 val = 0; |
61 | int ret = 0; |
62 | |
63 | memset(dt, 0, sizeof(*dt)); |
64 | |
65 | ret |= parse_timing_property(np, name: "hback-porch" , result: &dt->hback_porch); |
66 | ret |= parse_timing_property(np, name: "hfront-porch" , result: &dt->hfront_porch); |
67 | ret |= parse_timing_property(np, name: "hactive" , result: &dt->hactive); |
68 | ret |= parse_timing_property(np, name: "hsync-len" , result: &dt->hsync_len); |
69 | ret |= parse_timing_property(np, name: "vback-porch" , result: &dt->vback_porch); |
70 | ret |= parse_timing_property(np, name: "vfront-porch" , result: &dt->vfront_porch); |
71 | ret |= parse_timing_property(np, name: "vactive" , result: &dt->vactive); |
72 | ret |= parse_timing_property(np, name: "vsync-len" , result: &dt->vsync_len); |
73 | ret |= parse_timing_property(np, name: "clock-frequency" , result: &dt->pixelclock); |
74 | |
75 | dt->flags = 0; |
76 | if (!of_property_read_u32(np, propname: "vsync-active" , out_value: &val)) |
77 | dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : |
78 | DISPLAY_FLAGS_VSYNC_LOW; |
79 | if (!of_property_read_u32(np, propname: "hsync-active" , out_value: &val)) |
80 | dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : |
81 | DISPLAY_FLAGS_HSYNC_LOW; |
82 | if (!of_property_read_u32(np, propname: "de-active" , out_value: &val)) |
83 | dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : |
84 | DISPLAY_FLAGS_DE_LOW; |
85 | if (!of_property_read_u32(np, propname: "pixelclk-active" , out_value: &val)) |
86 | dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : |
87 | DISPLAY_FLAGS_PIXDATA_NEGEDGE; |
88 | |
89 | if (!of_property_read_u32(np, propname: "syncclk-active" , out_value: &val)) |
90 | dt->flags |= val ? DISPLAY_FLAGS_SYNC_POSEDGE : |
91 | DISPLAY_FLAGS_SYNC_NEGEDGE; |
92 | else if (dt->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | |
93 | DISPLAY_FLAGS_PIXDATA_NEGEDGE)) |
94 | dt->flags |= dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? |
95 | DISPLAY_FLAGS_SYNC_POSEDGE : |
96 | DISPLAY_FLAGS_SYNC_NEGEDGE; |
97 | |
98 | if (of_property_read_bool(np, propname: "interlaced" )) |
99 | dt->flags |= DISPLAY_FLAGS_INTERLACED; |
100 | if (of_property_read_bool(np, propname: "doublescan" )) |
101 | dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; |
102 | if (of_property_read_bool(np, propname: "doubleclk" )) |
103 | dt->flags |= DISPLAY_FLAGS_DOUBLECLK; |
104 | |
105 | if (ret) { |
106 | pr_err("%pOF: error reading timing properties\n" , np); |
107 | return -EINVAL; |
108 | } |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | /** |
114 | * of_get_display_timing - parse a display_timing entry |
115 | * @np: device_node with the timing subnode |
116 | * @name: name of the timing node |
117 | * @dt: display_timing struct to fill |
118 | **/ |
119 | int of_get_display_timing(const struct device_node *np, const char *name, |
120 | struct display_timing *dt) |
121 | { |
122 | struct device_node *timing_np; |
123 | int ret; |
124 | |
125 | if (!np) |
126 | return -EINVAL; |
127 | |
128 | timing_np = of_get_child_by_name(node: np, name); |
129 | if (!timing_np) |
130 | return -ENOENT; |
131 | |
132 | ret = of_parse_display_timing(np: timing_np, dt); |
133 | |
134 | of_node_put(node: timing_np); |
135 | |
136 | return ret; |
137 | } |
138 | EXPORT_SYMBOL_GPL(of_get_display_timing); |
139 | |
140 | /** |
141 | * of_get_display_timings - parse all display_timing entries from a device_node |
142 | * @np: device_node with the subnodes |
143 | **/ |
144 | struct display_timings *of_get_display_timings(const struct device_node *np) |
145 | { |
146 | struct device_node *timings_np; |
147 | struct device_node *entry; |
148 | struct device_node *native_mode; |
149 | struct display_timings *disp; |
150 | |
151 | if (!np) |
152 | return NULL; |
153 | |
154 | timings_np = of_get_child_by_name(node: np, name: "display-timings" ); |
155 | if (!timings_np) { |
156 | pr_err("%pOF: could not find display-timings node\n" , np); |
157 | return NULL; |
158 | } |
159 | |
160 | disp = kzalloc(size: sizeof(*disp), GFP_KERNEL); |
161 | if (!disp) { |
162 | pr_err("%pOF: could not allocate struct disp'\n" , np); |
163 | goto dispfail; |
164 | } |
165 | |
166 | entry = of_parse_phandle(np: timings_np, phandle_name: "native-mode" , index: 0); |
167 | /* assume first child as native mode if none provided */ |
168 | if (!entry) |
169 | entry = of_get_next_child(node: timings_np, NULL); |
170 | /* if there is no child, it is useless to go on */ |
171 | if (!entry) { |
172 | pr_err("%pOF: no timing specifications given\n" , np); |
173 | goto entryfail; |
174 | } |
175 | |
176 | pr_debug("%pOF: using %pOFn as default timing\n" , np, entry); |
177 | |
178 | native_mode = entry; |
179 | |
180 | disp->num_timings = of_get_child_count(np: timings_np); |
181 | if (disp->num_timings == 0) { |
182 | /* should never happen, as entry was already found above */ |
183 | pr_err("%pOF: no timings specified\n" , np); |
184 | goto entryfail; |
185 | } |
186 | |
187 | disp->timings = kcalloc(n: disp->num_timings, |
188 | size: sizeof(struct display_timing *), |
189 | GFP_KERNEL); |
190 | if (!disp->timings) { |
191 | pr_err("%pOF: could not allocate timings array\n" , np); |
192 | goto entryfail; |
193 | } |
194 | |
195 | disp->num_timings = 0; |
196 | disp->native_mode = 0; |
197 | |
198 | for_each_child_of_node(timings_np, entry) { |
199 | struct display_timing *dt; |
200 | int r; |
201 | |
202 | dt = kmalloc(size: sizeof(*dt), GFP_KERNEL); |
203 | if (!dt) { |
204 | pr_err("%pOF: could not allocate display_timing struct\n" , |
205 | np); |
206 | goto timingfail; |
207 | } |
208 | |
209 | r = of_parse_display_timing(np: entry, dt); |
210 | if (r) { |
211 | /* |
212 | * to not encourage wrong devicetrees, fail in case of |
213 | * an error |
214 | */ |
215 | pr_err("%pOF: error in timing %d\n" , |
216 | np, disp->num_timings + 1); |
217 | kfree(objp: dt); |
218 | goto timingfail; |
219 | } |
220 | |
221 | if (native_mode == entry) |
222 | disp->native_mode = disp->num_timings; |
223 | |
224 | disp->timings[disp->num_timings] = dt; |
225 | disp->num_timings++; |
226 | } |
227 | of_node_put(node: timings_np); |
228 | /* |
229 | * native_mode points to the device_node returned by of_parse_phandle |
230 | * therefore call of_node_put on it |
231 | */ |
232 | of_node_put(node: native_mode); |
233 | |
234 | pr_debug("%pOF: got %d timings. Using timing #%d as default\n" , |
235 | np, disp->num_timings, |
236 | disp->native_mode + 1); |
237 | |
238 | return disp; |
239 | |
240 | timingfail: |
241 | of_node_put(node: native_mode); |
242 | display_timings_release(disp); |
243 | disp = NULL; |
244 | entryfail: |
245 | kfree(objp: disp); |
246 | dispfail: |
247 | of_node_put(node: timings_np); |
248 | return NULL; |
249 | } |
250 | EXPORT_SYMBOL_GPL(of_get_display_timings); |
251 | |