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