aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2017-10-17 05:06:08 -0400
committerMaxime Ripard <maxime.ripard@free-electrons.com>2017-10-17 10:31:31 -0400
commit8b11aaface2b182ee2b36509843d746c453f8750 (patch)
tree9e364b2eb13df8da4c15b5f246715317e66211e6
parent4690803b09c6f1da33058336844091f8b7f3118f (diff)
drm/sun4i: Implement endpoint parsing using kfifo
The commit da82b8785eeb ("drm/sun4i: add components in breadth first traversal order") implemented a breadth first traversal of our device tree nodes graph. However, it was relying on the kernel linked lists, and those are not really safe for addition. Indeed, in a single pipeline stage, your first stage (ie, the mixer or fronted) will be queued, and it will be the final iteration of that list as far as list_for_each_entry_safe is concerned. Then, during that final iteration, we'll queue another element (the TCON or the backend) that list_for_each_entry_safe will not account for, and we will leave the loop without having iterated over all the elements. And since we won't have built our components list properly, the DRM driver will be left non-functional. We can instead use a kfifo to queue and enqueue components in-order, as was the original intention. This also has the benefit of removing any dynamic allocation, making the error handling path simpler too. The only thing we're losing is the ability to tell whether an element has already been queued, but that was only needed to remove spurious logs, and therefore purely cosmetic. This means that this commit effectively reverses e8afb7b67fba ("drm/sun4i: don't add components that are already in the queue"). Fixes: da82b8785eeb ("drm/sun4i: add components in breadth first traversal order") Reviewed-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Link: https://patchwork.freedesktop.org/patch/msgid/4ecb323e787918208f6a5d9f0ebba12c62583c98.1508231063.git-series.maxime.ripard@free-electrons.com
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c71
1 files changed, 13 insertions, 58 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index b5879d4620d8..a27efad9bc76 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -11,6 +11,7 @@
11 */ 11 */
12 12
13#include <linux/component.h> 13#include <linux/component.h>
14#include <linux/kfifo.h>
14#include <linux/of_graph.h> 15#include <linux/of_graph.h>
15#include <linux/of_reserved_mem.h> 16#include <linux/of_reserved_mem.h>
16 17
@@ -222,29 +223,15 @@ static int compare_of(struct device *dev, void *data)
222 * matching system handles this for us. 223 * matching system handles this for us.
223 */ 224 */
224struct endpoint_list { 225struct endpoint_list {
225 struct device_node *node; 226 DECLARE_KFIFO(fifo, struct device_node *, 16);
226 struct list_head list;
227}; 227};
228 228
229static bool node_is_in_list(struct list_head *endpoints,
230 struct device_node *node)
231{
232 struct endpoint_list *endpoint;
233
234 list_for_each_entry(endpoint, endpoints, list)
235 if (endpoint->node == node)
236 return true;
237
238 return false;
239}
240
241static int sun4i_drv_add_endpoints(struct device *dev, 229static int sun4i_drv_add_endpoints(struct device *dev,
242 struct list_head *endpoints, 230 struct endpoint_list *list,
243 struct component_match **match, 231 struct component_match **match,
244 struct device_node *node) 232 struct device_node *node)
245{ 233{
246 struct device_node *port, *ep, *remote; 234 struct device_node *port, *ep, *remote;
247 struct endpoint_list *endpoint;
248 int count = 0; 235 int count = 0;
249 236
250 /* 237 /*
@@ -304,19 +291,7 @@ static int sun4i_drv_add_endpoints(struct device *dev,
304 } 291 }
305 } 292 }
306 293
307 /* skip downstream node if it is already in the queue */ 294 kfifo_put(&list->fifo, remote);
308 if (node_is_in_list(endpoints, remote))
309 continue;
310
311 /* Add downstream nodes to the queue */
312 endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
313 if (!endpoint) {
314 of_node_put(remote);
315 return -ENOMEM;
316 }
317
318 endpoint->node = remote;
319 list_add_tail(&endpoint->list, endpoints);
320 } 295 }
321 296
322 return count; 297 return count;
@@ -325,10 +300,11 @@ static int sun4i_drv_add_endpoints(struct device *dev,
325static int sun4i_drv_probe(struct platform_device *pdev) 300static int sun4i_drv_probe(struct platform_device *pdev)
326{ 301{
327 struct component_match *match = NULL; 302 struct component_match *match = NULL;
328 struct device_node *np = pdev->dev.of_node; 303 struct device_node *np = pdev->dev.of_node, *endpoint;
329 struct endpoint_list *endpoint, *endpoint_temp; 304 struct endpoint_list list;
330 int i, ret, count = 0; 305 int i, ret, count = 0;
331 LIST_HEAD(endpoints); 306
307 INIT_KFIFO(list.fifo);
332 308
333 for (i = 0;; i++) { 309 for (i = 0;; i++) {
334 struct device_node *pipeline = of_parse_phandle(np, 310 struct device_node *pipeline = of_parse_phandle(np,
@@ -337,31 +313,19 @@ static int sun4i_drv_probe(struct platform_device *pdev)
337 if (!pipeline) 313 if (!pipeline)
338 break; 314 break;
339 315
340 endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); 316 kfifo_put(&list.fifo, pipeline);
341 if (!endpoint) {
342 ret = -ENOMEM;
343 goto err_free_endpoints;
344 }
345
346 endpoint->node = pipeline;
347 list_add_tail(&endpoint->list, &endpoints);
348 } 317 }
349 318
350 list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) { 319 while (kfifo_get(&list.fifo, &endpoint)) {
351 /* process this endpoint */ 320 /* process this endpoint */
352 ret = sun4i_drv_add_endpoints(&pdev->dev, &endpoints, &match, 321 ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match,
353 endpoint->node); 322 endpoint);
354 323
355 /* sun4i_drv_add_endpoints can fail to allocate memory */ 324 /* sun4i_drv_add_endpoints can fail to allocate memory */
356 if (ret < 0) 325 if (ret < 0)
357 goto err_free_endpoints; 326 return ret;
358 327
359 count += ret; 328 count += ret;
360
361 /* delete and cleanup the current entry */
362 list_del(&endpoint->list);
363 of_node_put(endpoint->node);
364 kfree(endpoint);
365 } 329 }
366 330
367 if (count) 331 if (count)
@@ -370,15 +334,6 @@ static int sun4i_drv_probe(struct platform_device *pdev)
370 match); 334 match);
371 else 335 else
372 return 0; 336 return 0;
373
374err_free_endpoints:
375 list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) {
376 list_del(&endpoint->list);
377 of_node_put(endpoint->node);
378 kfree(endpoint);
379 }
380
381 return ret;
382} 337}
383 338
384static int sun4i_drv_remove(struct platform_device *pdev) 339static int sun4i_drv_remove(struct platform_device *pdev)