A few drivers in drivers/gpu/drm are component-enabled and use quite similar code sequences to probe for their encoder slaves at the remote end of the ports. Move the code into a "generic" function and remove it from the drivers.
The end results is that drivers get a reference count fix (imx), more thorough error checking (imx again), setting the DMA coherent mask (armada) plus a decrease in the overall count of LoC.
I'm looking for comments and testing of the patchset (only compile tested from my end as I don't have access to all the devices touched by the changes). My main interest is in finding out if -EINVAL is the correct code to return if dev->of_node == NULL (handy now, as it is different from the other possible error codes and used in armada to trigger old platform_data support. Also looking for thoughts on the correctness of the patch and if it possible to co-opt more drivers into using the function.
Best regards, Liviu
Liviu Dudau (4): drm: Introduce generic probe function for component based masters. drm/imx: Convert the probe function to the generic drm_of_component_probe() drm/rockchip: Convert the probe function to the generic drm_of_component_probe() drm/armada: Convert the probe function to the generic drm_of_component_probe()
drivers/gpu/drm/armada/armada_drv.c | 83 +++++++++----------------- drivers/gpu/drm/drm_of.c | 92 +++++++++++++++++++++++++++++ drivers/gpu/drm/imx/imx-drm-core.c | 54 +---------------- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 84 ++------------------------ include/drm/drm_of.h | 12 ++++ 5 files changed, 138 insertions(+), 187 deletions(-)
A lot of component based DRM drivers use a variant of the same code as the probe function. They bind the crtc ports in the first iteration and then scan through the child nodes and bind the encoders attached to the remote endpoints. Factor the common code into a separate function called drm_of_component_probe() in order to increase code reuse.
Cc: David Airlie airlied@linux.ie Signed-off-by: Liviu Dudau Liviu.Dudau@arm.com --- drivers/gpu/drm/drm_of.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_of.h | 12 +++++++ 2 files changed, 104 insertions(+)
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index be38840..cf16240 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -1,3 +1,4 @@ +#include <linux/component.h> #include <linux/export.h> #include <linux/list.h> #include <linux/of_graph.h> @@ -61,3 +62,94 @@ uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, return possible_crtcs; } EXPORT_SYMBOL(drm_of_find_possible_crtcs); + +/** + * drm_of_component_probe - Generic probe function for a component based master + * @dev: master device containing the OF node + * @compare_of: compare function used for matching components + * @master_ops: component master ops to be used + * + * Parse the platform device OF node and bind all the components associated + * with the master. Interface ports are added before the encoders in order to + * satisfy their .bind requirements + * See Documentation/devicetree/bindings/graph.txt for the bindings. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_component_probe(struct device *dev, + int (*compare_of)(struct device *, void *), + const struct component_master_ops *master_ops) +{ + struct device_node *ep, *port, *remote; + struct component_match *match = NULL; + int i, ret; + + if (!dev->of_node) + return -EINVAL; + + /* + * Bind the crtc's ports first, so that drm_of_find_possible_crtcs() + * called from encoder's .bind callbacks works as expected + */ + for (i = 0; ; i++) { + port = of_parse_phandle(dev->of_node, "ports", i); + if (!port) + break; + + if (!of_device_is_available(port->parent)) { + of_node_put(port); + continue; + } + + component_match_add(dev, &match, compare_of, port); + of_node_put(port); + } + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + if (!match) { + dev_err(dev, "no available port\n"); + return -ENODEV; + } + + /* + * For bound crtcs, bind the encoders attached to their remote endpoint + */ + for (i = 0; ; i++) { + port = of_parse_phandle(dev->of_node, "ports", i); + if (!port) + break; + + if (!of_device_is_available(port->parent)) { + of_node_put(port); + continue; + } + + for_each_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; + } + + component_match_add(dev, &match, compare_of, remote); + of_node_put(remote); + } + of_node_put(port); + } + + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return component_master_add_with_match(dev, master_ops, match); +} +EXPORT_SYMBOL(drm_of_component_probe); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 2441f71..7c0c05b 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -1,18 +1,30 @@ #ifndef __DRM_OF_H__ #define __DRM_OF_H__
+struct component_master_ops; +struct device; struct drm_device; struct device_node;
#ifdef CONFIG_OF extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port); +extern int drm_of_component_probe(struct device *dev, + int (*compare_of)(struct device *, void *), + const struct component_master_ops *master_ops); #else static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port) { return 0; } + +static inline int drm_of_component_probe(struct device *dev, + int (*compare_of)(struct device *, void *), + const struct component_master_ops *master_ops) +{ + return -EINVAL; +} #endif
#endif /* __DRM_OF_H__ */
The generic function is functionally equivalent to the driver's imx_drm_platform_probe(). Use the generic function and reduce the overall code size.
Signed-off-by: Liviu Dudau Liviu.Dudau@arm.com --- drivers/gpu/drm/imx/imx-drm-core.c | 54 +------------------------------------- 1 file changed, 1 insertion(+), 53 deletions(-)
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 74f505b..c25e39d 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -531,59 +531,7 @@ static const struct component_master_ops imx_drm_ops = {
static int imx_drm_platform_probe(struct platform_device *pdev) { - struct device_node *ep, *port, *remote; - struct component_match *match = NULL; - int ret; - int i; - - /* - * Bind the IPU display interface ports first, so that - * imx_drm_encoder_parse_of called from encoder .bind callbacks - * works as expected. - */ - for (i = 0; ; i++) { - port = of_parse_phandle(pdev->dev.of_node, "ports", i); - if (!port) - break; - - component_match_add(&pdev->dev, &match, compare_of, port); - } - - if (i == 0) { - dev_err(&pdev->dev, "missing 'ports' property\n"); - return -ENODEV; - } - - /* Then bind all encoders */ - for (i = 0; ; i++) { - port = of_parse_phandle(pdev->dev.of_node, "ports", i); - if (!port) - break; - - for_each_child_of_node(port, ep) { - remote = of_graph_get_remote_port_parent(ep); - if (!remote || !of_device_is_available(remote)) { - of_node_put(remote); - continue; - } else if (!of_device_is_available(remote->parent)) { - dev_warn(&pdev->dev, "parent device of %s is not available\n", - remote->full_name); - of_node_put(remote); - continue; - } - - component_match_add(&pdev->dev, &match, compare_of, - remote); - of_node_put(remote); - } - of_node_put(port); - } - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - return component_master_add_with_match(&pdev->dev, &imx_drm_ops, match); + return drm_of_component_probe(&pdev->dev, compare_of, &imx_drm_ops); }
static int imx_drm_platform_remove(struct platform_device *pdev)
Use the generic drm_of_component_probe() function to probe for components.
Signed-off-by: Liviu Dudau Liviu.Dudau@arm.com --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 84 ++--------------------------- 1 file changed, 5 insertions(+), 79 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 9a0c291..8cd6397 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -47,10 +47,6 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping; int ret;
- ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
return arm_iommu_attach_device(dev, mapping); @@ -416,29 +412,6 @@ static int compare_of(struct device *dev, void *data) return dev->of_node == np; }
-static void rockchip_add_endpoints(struct device *dev, - struct component_match **match, - struct device_node *port) -{ - struct device_node *ep, *remote; - - for_each_child_of_node(port, ep) { - remote = of_graph_get_remote_port_parent(ep); - if (!remote || !of_device_is_available(remote)) { - of_node_put(remote); - continue; - } else if (!of_device_is_available(remote->parent)) { - dev_warn(dev, "parent device of %s is not available\n", - remote->full_name); - of_node_put(remote); - continue; - } - - component_match_add(dev, match, compare_of, remote); - of_node_put(remote); - } -} - static int rockchip_drm_bind(struct device *dev) { struct drm_device *drm; @@ -481,61 +454,14 @@ static const struct component_master_ops rockchip_drm_ops = {
static int rockchip_drm_platform_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct component_match *match = NULL; - struct device_node *np = dev->of_node; - struct device_node *port; - int i; - - if (!np) - return -ENODEV; - /* - * Bind the crtc ports first, so that - * drm_of_find_possible_crtcs called from encoder .bind callbacks - * works as expected. - */ - for (i = 0;; i++) { - port = of_parse_phandle(np, "ports", i); - if (!port) - break; - - if (!of_device_is_available(port->parent)) { - of_node_put(port); - continue; - } + int ret = drm_of_component_probe(&pdev->dev, compare_of, + &rockchip_drm_ops);
- component_match_add(dev, &match, compare_of, port->parent); - of_node_put(port); - } - - if (i == 0) { - dev_err(dev, "missing 'ports' property\n"); + /* keep compatibility with old code that was returning -ENODEV */ + if (ret == -EINVAL) return -ENODEV; - } - - if (!match) { - dev_err(dev, "No available vop found for display-subsystem.\n"); - return -ENODEV; - } - /* - * For each bound crtc, bind the encoders attached to its - * remote endpoint. - */ - for (i = 0;; i++) { - port = of_parse_phandle(np, "ports", i); - if (!port) - break; - - if (!of_device_is_available(port->parent)) { - of_node_put(port); - continue; - }
- rockchip_add_endpoints(dev, &match, port); - of_node_put(port); - } - - return component_master_add_with_match(dev, &rockchip_drm_ops, match); + return ret; }
static int rockchip_drm_platform_remove(struct platform_device *pdev)
Hi Liviu,
[auto build test ERROR on drm/drm-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]
url: https://github.com/0day-ci/linux/commits/Liviu-Dudau/drm-Introduce-generic-p... config: arm-allmodconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/ma... -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm
All error/warnings (new ones prefixed by >>):
drivers/gpu/drm/rockchip/rockchip_drm_drv.c: In function 'rockchip_drm_dma_attach_device':
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:48:6: warning: unused variable 'ret' [-Wunused-variable]
int ret; ^ drivers/gpu/drm/rockchip/rockchip_drm_drv.c: In function 'rockchip_drm_platform_probe':
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:457:2: error: implicit declaration of function 'drm_of_component_probe' [-Werror=implicit-function-declaration]
int ret = drm_of_component_probe(&pdev->dev, compare_of, ^ cc1: some warnings being treated as errors
vim +/drm_of_component_probe +457 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
451 .bind = rockchip_drm_bind, 452 .unbind = rockchip_drm_unbind, 453 }; 454 455 static int rockchip_drm_platform_probe(struct platform_device *pdev) 456 {
457 int ret = drm_of_component_probe(&pdev->dev, compare_of,
458 &rockchip_drm_ops); 459 460 /* keep compatibility with old code that was returning -ENODEV */
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
The armada DRM driver keeps some old platform data compatibility in the probe function that makes moving to the generic drm_of_component_probe() a bit more complicated that it should. Refactor the probe function to do the platform_data processing after the generic probe (and if that fails). This way future cleanup can further remove support for it.
Signed-off-by: Liviu Dudau Liviu.Dudau@arm.com --- drivers/gpu/drm/armada/armada_drv.c | 83 +++++++++++++------------------------ 1 file changed, 28 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 225034b..990080d 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -11,6 +11,7 @@ #include <linux/of_graph.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_gem.h" @@ -370,47 +371,36 @@ static void armada_add_endpoints(struct device *dev, } }
-static int armada_drm_find_components(struct device *dev, - struct component_match **match) -{ - struct device_node *port; - int i; - - if (dev->of_node) { - struct device_node *np = dev->of_node; - - for (i = 0; ; i++) { - port = of_parse_phandle(np, "ports", i); - if (!port) - break; +static const struct component_master_ops armada_master_ops = { + .bind = armada_drm_bind, + .unbind = armada_drm_unbind, +};
- component_match_add(dev, match, compare_of, port); - of_node_put(port); - } +static int armada_drm_probe(struct platform_device *pdev) +{ + int ret;
- if (i == 0) { - dev_err(dev, "missing 'ports' property\n"); - return -ENODEV; - } + if (!is_componentized(&pdev->dev)) + return drm_platform_init(&armada_drm_driver, pdev);
- for (i = 0; ; i++) { - port = of_parse_phandle(np, "ports", i); - if (!port) - break; + ret = drm_of_component_probe(&pdev->dev, compare_dev_name, + &armada_master_ops); + if (ret != -EINVAL) + return ret;
- armada_add_endpoints(dev, match, port); - of_node_put(port); - } - } else if (dev->platform_data) { - char **devices = dev->platform_data; + if (pdev->dev.platform_data) { + struct component_match *match = NULL; + char **devices = pdev->dev.platform_data; + struct device_node *port; struct device *d; + int i;
for (i = 0; devices[i]; i++) - component_match_add(dev, match, compare_dev_name, + component_match_add(&pdev->dev, &match, compare_dev_name, devices[i]);
if (i == 0) { - dev_err(dev, "missing 'ports' property\n"); + dev_err(&pdev->dev, "missing 'ports' property\n"); return -ENODEV; }
@@ -419,35 +409,18 @@ static int armada_drm_find_components(struct device *dev, devices[i]); if (d && d->of_node) { for_each_child_of_node(d->of_node, port) - armada_add_endpoints(dev, match, port); + armada_add_endpoints(&pdev->dev, + &match, port); } put_device(d); } - } - - return 0; -} - -static const struct component_master_ops armada_master_ops = { - .bind = armada_drm_bind, - .unbind = armada_drm_unbind, -};
-static int armada_drm_probe(struct platform_device *pdev) -{ - if (is_componentized(&pdev->dev)) { - struct component_match *match = NULL; - int ret; - - ret = armada_drm_find_components(&pdev->dev, &match); - if (ret < 0) - return ret; - - return component_master_add_with_match(&pdev->dev, - &armada_master_ops, match); - } else { - return drm_platform_init(&armada_drm_driver, pdev); + ret = component_master_add_with_match(&pdev->dev, + &armada_master_ops, + match); } + + return ret; }
static int armada_drm_remove(struct platform_device *pdev)
dri-devel@lists.freedesktop.org