mbox series

[v5,0/8] Refactor MTK MDP driver into core/components

Message ID 20210709022324.1607884-1-eizan@chromium.org
Headers show
Series Refactor MTK MDP driver into core/components | expand

Message

Eizan Miyamoto July 9, 2021, 2:23 a.m. UTC
This is an update to
https://patchwork.kernel.org/project/linux-mediatek/list/?series=283075
To address some comments and fixes.

This series has been verified to work on 5.13.


Changes in v5:
- rebase and build-test on 5.13-next @ e2f74b13dbe6

Changes in v4:
- rebase and test on 5.13
- don't depend on https://patchwork.kernel.org/project/linux-mediatek/list/?series=464873

Changes in v3:
- get mdp master from aliases instead of strcmp against of_node->name

Changes in v2:
- rebased onto Linux 5.12
- 100 char line length allowance was utilized in a few places
- Removal of a redundant dev_err() print at the end of
  mtk_mdp_comp_init()
- Instead of printing errors and ignoring them, I've added a patch to
  correctly propagate them.
- Use of C style comments.
- Three additional patches were added to eliminate dependency on the
  mediatek,vpu property inside the mdp_rdma0 device node.

Eizan Miyamoto (8):
  mtk-mdp: add driver to probe mdp components
  mtk-mdp: use pm_runtime in MDP component driver
  media: mtk-mdp: don't pm_run_time_get/put for master comp in clock_on
  mtk-mdp: soc: mediatek: register mdp from mmsys
  media: mtk-mdp: search for vpu node instead of linking it to a
    property
  media: mtk-mdp: propagate errors better in pm_suspend/resume
  media: mtk-mdp: use mdp-rdma0 alias to point to MDP master
  dts: mtk-mdp: remove mediatek,vpu property from primary MDP device

 arch/arm64/boot/dts/mediatek/mt8173.dtsi      |   1 -
 drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 272 +++++++++++++++--
 drivers/media/platform/mtk-mdp/mtk_mdp_comp.h |  36 +--
 drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 281 ++++++++++++------
 drivers/media/platform/mtk-mdp/mtk_mdp_core.h |   3 +
 drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c  |   4 +-
 drivers/soc/mediatek/mtk-mmsys.c              |  20 +-
 7 files changed, 473 insertions(+), 144 deletions(-)

Comments

Enric Balletbo i Serra July 12, 2021, 10:11 a.m. UTC | #1
Hi Eizan,

Thank you for your patch.

On 9/7/21 4:23, Eizan Miyamoto wrote:
> Broadly, this patch (1) adds a driver for various MTK MDP components to

> go alongside the main MTK MDP driver, and (2) hooks them all together

> using the component framework.

> 

> (1) Up until now, the MTK MDP driver controls 8 devices in the device

> tree on its own. When running tests for the hardware video decoder, we

> found that the iommus and LARBs were not being properly configured. To

> configure them, a driver for each be added to mtk_mdp_comp so that

> mtk_iommu_add_device() can (eventually) be called from dma_configure()

> inside really_probe().

> 

> (2) The integration into the component framework allows us to defer the

> registration with the v4l2 subsystem until all the MDP-related devices

> have been probed, so that the relevant device node does not become

> available until initialization of all the components is complete.

> 

> Some notes about how the component framework has been integrated:

> 

> - The driver for the rdma0 component serves double duty as the "master"

>   (aggregate) driver as well as a component driver. This is a non-ideal

>   compromise until a better solution is developed. This device is

>   differentiated from the rest by checking for a "mediatek,vpu" property

>   in the device node.

> 

> - The list of mdp components remains hard-coded as mtk_mdp_comp_dt_ids[]

>   in mtk_mdp_core.c, and as mtk_mdp_comp_driver_dt_match[] in

>   mtk_mdp_comp.c. This unfortunate duplication of information is

>   addressed in a following patch in this series.

> 

> - The component driver calls component_add() for each device that is

>   probed.

> 

> - In mtk_mdp_probe (the "master" device), we scan the device tree for

>   any matching nodes against mtk_mdp_comp_dt_ids, and add component

>   matches for them. The match criteria is a matching device node

>   pointer.

> 

> - When the set of components devices that have been probed corresponds

>   with the list that is generated by the "master", the callback to

>   mtk_mdp_master_bind() is made, which then calls the component bind

>   functions.

> 

> - Inside mtk_mdp_master_bind(), once all the component bind functions

>   have been called, we can then register our device to the v4l2

>   subsystem.

> 

> - The call to pm_runtime_enable() in the master device is called after

>   all the components have been registered by their bind() functions

>   called by mtk_mtp_master_bind(). As a result, the list of components

>   will not change while power management callbacks mtk_mdp_suspend()/

>   resume() are accessing the list of components.

> 

> Signed-off-by: Eizan Miyamoto <eizan@chromium.org>

> ---

> 

> (no changes since v1)

> 

>  drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 156 +++++++++++++---

>  drivers/media/platform/mtk-mdp/mtk_mdp_comp.h |  25 +--

>  drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 175 +++++++++++++-----

>  drivers/media/platform/mtk-mdp/mtk_mdp_core.h |   1 +

>  4 files changed, 262 insertions(+), 95 deletions(-)

> 

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> index b3426a551bea..aec1cb5c4261 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> @@ -5,14 +5,52 @@

>   */

>  

>  #include <linux/clk.h>

> +#include <linux/component.h>

>  #include <linux/device.h>

> -#include <linux/of.h>


You're removing the include ... [1]

> +#include <linux/module.h>

>  #include <linux/of_address.h>

> +#include <linux/of_device.h>


I think this include is not needed, please remove it.

> +#include <linux/of.h>


[1] and adding here again, this change is not needed.

> +#include <linux/of_irq.h>


I think this include is not needed, please remove it.

>  #include <linux/of_platform.h>

>  #include <soc/mediatek/smi.h>

>  

>  #include "mtk_mdp_comp.h"

> -

> +#include "mtk_mdp_core.h"

> +

> +/**

> + * enum mtk_mdp_comp_type - the MDP component

> + * @MTK_MDP_RDMA:		Read DMA

> + * @MTK_MDP_RSZ:		Reszer

> + * @MTK_MDP_WDMA:		Write DMA

> + * @MTK_MDP_WROT:		Write DMA with rotation

> + * @MTK_MDP_COMP_TYPE_MAX:	Placeholder for num elems in this enum

> + */

> +enum mtk_mdp_comp_type {

> +	MTK_MDP_RDMA,

> +	MTK_MDP_RSZ,

> +	MTK_MDP_WDMA,

> +	MTK_MDP_WROT,

> +	MTK_MDP_COMP_TYPE_MAX,


This is not used anywhere, so you can remove it. Introduce it when you need (if
is needed).

> +};

> +

> +static const struct of_device_id mtk_mdp_comp_driver_dt_match[] = {

> +	{

> +		.compatible = "mediatek,mt8173-mdp-rdma",

> +		.data = (void *)MTK_MDP_RDMA

> +	}, {

> +		.compatible = "mediatek,mt8173-mdp-rsz",

> +		.data = (void *)MTK_MDP_RSZ

> +	}, {

> +		.compatible = "mediatek,mt8173-mdp-wdma",

> +		.data = (void *)MTK_MDP_WDMA

> +	}, {

> +		.compatible = "mediatek,mt8173-mdp-wrot",

> +		.data = (void *)MTK_MDP_WROT

> +	},

> +	{ }

> +};

> +MODULE_DEVICE_TABLE(of, mtk_mdp_comp_driver_dt_match);

>  

>  void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)

>  {

> @@ -21,9 +59,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)

>  	if (comp->larb_dev) {

>  		err = mtk_smi_larb_get(comp->larb_dev);

>  		if (err)

> -			dev_err(dev,

> -				"failed to get larb, err %d. type:%d\n",

> -				err, comp->type);

> +			dev_err(comp->dev,

> +				"failed to get larb, err %d.\n", err);


I think that if you apply this patch alone it will fail to build. AFAICS there
is no member named 'dev' in 'struct mtk_mdp_comp'.

nit: Now that the limit is 100 chars, this can be in one line.

>  	}

>  

>  	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {

> @@ -32,8 +69,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)

>  		err = clk_prepare_enable(comp->clk[i]);

>  		if (err)

>  			dev_err(dev,

> -			"failed to enable clock, err %d. type:%d i:%d\n",

> -				err, comp->type, i);

> +				"failed to enable clock, err %d. i:%d\n",

> +				err, i);


nit: Now that the limit is 100 chars, this can be in one line.

>  	}

>  }

>  

> @@ -51,17 +88,42 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp)

>  		mtk_smi_larb_put(comp->larb_dev);

>  }

>  

> -int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

> -		      struct mtk_mdp_comp *comp,

> -		      enum mtk_mdp_comp_type comp_type)

> +static int mtk_mdp_comp_bind(struct device *dev, struct device *master,

> +			void *data)


nit: Now that the limit is 100 chars, this can be in one line.

> +{

> +	struct mtk_mdp_comp *comp = dev_get_drvdata(dev);

> +	struct mtk_mdp_dev *mdp = data;

> +

> +	mtk_mdp_register_component(mdp, comp);

> +

> +	return 0;

> +}

> +

> +static void mtk_mdp_comp_unbind(struct device *dev, struct device *master,

> +			   void *data)

> +{

> +	struct mtk_mdp_dev *mdp = data;

> +	struct mtk_mdp_comp *comp = dev_get_drvdata(dev);


nit: reverse x-tree order, like you did just before. First comp, and then mdp.

> +

> +	mtk_mdp_unregister_component(mdp, comp);

> +}

> +

> +static const struct component_ops mtk_mdp_component_ops = {

> +	.bind   = mtk_mdp_comp_bind,

> +	.unbind = mtk_mdp_comp_unbind,

> +};

> +

> +int mtk_mdp_comp_init(struct mtk_mdp_comp *comp, struct device *dev)

>  {

>  	struct device_node *larb_node;

>  	struct platform_device *larb_pdev;

>  	int ret;

>  	int i;

> +	struct device_node *node = dev->of_node;

> +	enum mtk_mdp_comp_type comp_type =

> +		 (enum mtk_mdp_comp_type)of_device_get_match_data(dev);


nit: Now that the limit is 100 chars, this can be in one line. Also maybe apply
the reverse x-tree rule for the new variables?

>  

> -	comp->dev_node = of_node_get(node);

> -	comp->type = comp_type;

> +	INIT_LIST_HEAD(&comp->node);

>  

>  	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {

>  		comp->clk[i] = of_clk_get(node, i);

> @@ -69,19 +131,19 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

>  			if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER)

>  				dev_err(dev, "Failed to get clock\n");

>  			ret = PTR_ERR(comp->clk[i]);

> -			goto put_dev;

> +			goto err;

>  		}

>  

>  		/* Only RDMA needs two clocks */

> -		if (comp->type != MTK_MDP_RDMA)

> +		if (comp_type != MTK_MDP_RDMA)

>  			break;

>  	}

>  

>  	/* Only DMA capable components need the LARB property */

>  	comp->larb_dev = NULL;

> -	if (comp->type != MTK_MDP_RDMA &&

> -	    comp->type != MTK_MDP_WDMA &&

> -	    comp->type != MTK_MDP_WROT)

> +	if (comp_type != MTK_MDP_RDMA &&

> +	    comp_type != MTK_MDP_WDMA &&

> +	    comp_type != MTK_MDP_WROT)


nit: Now that the limit is 100 chars, this can be in one line.

>  		return 0;

>  

>  	larb_node = of_parse_phandle(node, "mediatek,larb", 0);

> @@ -89,7 +151,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

>  		dev_err(dev,

>  			"Missing mediadek,larb phandle in %pOF node\n", node);

>  		ret = -EINVAL;

> -		goto put_dev;

> +		goto err;

>  	}

>  

>  	larb_pdev = of_find_device_by_node(larb_node);

> @@ -97,7 +159,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

>  		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);

>  		of_node_put(larb_node);

>  		ret = -EPROBE_DEFER;

> -		goto put_dev;

> +		goto err;

>  	}

>  	of_node_put(larb_node);

>  

> @@ -105,13 +167,59 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

>  

>  	return 0;

>  

> -put_dev:

> -	of_node_put(comp->dev_node);

> -

> +err:

>  	return ret;

>  }

>  

> -void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp)

> +static int mtk_mdp_comp_probe(struct platform_device *pdev)

> +{

> +	struct device *dev = &pdev->dev;

> +	struct device_node *vpu_node;

> +	int status;

> +	struct mtk_mdp_comp *comp;


nit: reverse x-tree order.

> +

> +	vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0);

> +	if (vpu_node) {

> +		of_node_put(vpu_node);

> +		/*

> +		 * The device tree node with a mediatek,vpu property is deemed

> +		 * the MDP "master" device, we don't want to add a component

> +		 * for it in this function because the initialization for the

> +		 * master is done elsewhere.

> +		 */

> +		dev_info(dev, "vpu node found, not probing\n");

> +		return -ENODEV;


For me this is confusing, so you're returning and error but what really want is
just ignore it. On the sub-system I maintain I'd prefer to reduce the dev_info
to dev_dbg and just return 0 here, as is not and error. But I think this code is
removed later, so not sure how important is to change that.


> +	}

> +

> +	comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);

> +	if (!comp)

> +		return -ENOMEM;

> +

> +	status = mtk_mdp_comp_init(comp, dev);

> +	if (status) {

> +		dev_err(dev, "Failed to initialize component: %d\n", status);

> +		return status;

> +	}

> +

> +	dev_set_drvdata(dev, comp);

> +

> +	return component_add(dev, &mtk_mdp_component_ops);

> +}

> +

> +static int mtk_mdp_comp_remove(struct platform_device *pdev)

>  {

> -	of_node_put(comp->dev_node);

> +	struct device *dev = &pdev->dev;

> +

> +	component_del(dev, &mtk_mdp_component_ops);

> +	return 0;

>  }

> +

> +struct platform_driver mtk_mdp_component_driver = {

> +	.probe          = mtk_mdp_comp_probe,

> +	.remove         = mtk_mdp_comp_remove,

> +	.driver         = {

> +		.name   = "mediatek-mdp-comp",

> +		.owner  = THIS_MODULE,

> +		.of_match_table = mtk_mdp_comp_driver_dt_match,

> +	},

> +};

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> index 7897766c96bb..b5a18fe567aa 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> @@ -7,42 +7,23 @@

>  #ifndef __MTK_MDP_COMP_H__

>  #define __MTK_MDP_COMP_H__

>  

> -/**

> - * enum mtk_mdp_comp_type - the MDP component

> - * @MTK_MDP_RDMA:	Read DMA

> - * @MTK_MDP_RSZ:	Riszer

> - * @MTK_MDP_WDMA:	Write DMA

> - * @MTK_MDP_WROT:	Write DMA with rotation

> - */

> -enum mtk_mdp_comp_type {

> -	MTK_MDP_RDMA,

> -	MTK_MDP_RSZ,

> -	MTK_MDP_WDMA,

> -	MTK_MDP_WROT,

> -};

> -

>  /**

>   * struct mtk_mdp_comp - the MDP's function component data

>   * @node:	list node to track sibing MDP components

> - * @dev_node:	component device node

>   * @clk:	clocks required for component

>   * @larb_dev:	SMI device required for component

> - * @type:	component type

>   */

>  struct mtk_mdp_comp {

>  	struct list_head	node;

> -	struct device_node	*dev_node;

>  	struct clk		*clk[2];

>  	struct device		*larb_dev;

> -	enum mtk_mdp_comp_type	type;

>  };

>  

> -int mtk_mdp_comp_init(struct device *dev, struct device_node *node,

> -		      struct mtk_mdp_comp *comp,

> -		      enum mtk_mdp_comp_type comp_type);

> -void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp);

> +int mtk_mdp_comp_init(struct mtk_mdp_comp *comp, struct device *dev);

> +

>  void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp);

>  void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp);

>  

> +extern struct platform_driver mtk_mdp_component_driver;

>  

>  #endif /* __MTK_MDP_COMP_H__ */

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> index 976aa1f4829b..aca236dbd348 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> @@ -6,6 +6,7 @@

>   */

>  

>  #include <linux/clk.h>

> +#include <linux/component.h>

>  #include <linux/device.h>

>  #include <linux/errno.h>

>  #include <linux/interrupt.h>

> @@ -19,6 +20,7 @@

>  #include <linux/workqueue.h>

>  #include <soc/mediatek/smi.h>

>  

> +#include "mtk_mdp_comp.h"

>  #include "mtk_mdp_core.h"

>  #include "mtk_mdp_m2m.h"

>  #include "mtk_vpu.h"

> @@ -32,16 +34,12 @@ module_param(mtk_mdp_dbg_level, int, 0644);

>  static const struct of_device_id mtk_mdp_comp_dt_ids[] = {

>  	{

>  		.compatible = "mediatek,mt8173-mdp-rdma",

> -		.data = (void *)MTK_MDP_RDMA

>  	}, {

>  		.compatible = "mediatek,mt8173-mdp-rsz",

> -		.data = (void *)MTK_MDP_RSZ

>  	}, {

>  		.compatible = "mediatek,mt8173-mdp-wdma",

> -		.data = (void *)MTK_MDP_WDMA

>  	}, {

>  		.compatible = "mediatek,mt8173-mdp-wrot",

> -		.data = (void *)MTK_MDP_WROT

>  	},

>  	{ },

>  };

> @@ -91,6 +89,64 @@ static void mtk_mdp_reset_handler(void *priv)

>  	queue_work(mdp->wdt_wq, &mdp->wdt_work);

>  }

>  

> +static int compare_of(struct device *dev, void *data)

> +{

> +	return dev->of_node == data;

> +}

> +

> +static void release_of(struct device *dev, void *data)

> +{

> +	of_node_put(data);

> +}

> +

> +static int mtk_mdp_master_bind(struct device *dev)

> +{

> +	int status;

> +	struct mtk_mdp_dev *mdp = dev_get_drvdata(dev);


nit: reverse x-tree

> +

> +	mtk_mdp_register_component(mdp, &mdp->comp_self);

> +

> +	status = component_bind_all(dev, mdp);

> +	if (status) {

> +		dev_err(dev, "Failed to bind all components: %d\n", status);

> +		goto err_component_bind_all;

> +	}

> +

> +	status = mtk_mdp_register_m2m_device(mdp);

> +	if (status) {

> +		dev_err(dev, "Failed to register m2m device: %d\n", status);

> +		v4l2_err(&mdp->v4l2_dev, "Failed to init mem2mem device\n");


Duplicated error message, so remove one of them.

> +		goto err_mtk_mdp_register_m2m_device;

> +	}

> +

> +	pm_runtime_enable(dev);

> +

> +	return 0;

> +

> +err_mtk_mdp_register_m2m_device:

> +	component_unbind_all(dev, mdp);

> +

> +err_component_bind_all:

> +	mtk_mdp_unregister_component(mdp, &mdp->comp_self);

> +

> +	return status;

> +}

> +

> +static void mtk_mdp_master_unbind(struct device *dev)

> +{

> +	struct mtk_mdp_dev *mdp = dev_get_drvdata(dev);

> +

> +	pm_runtime_disable(dev);

> +	mtk_mdp_unregister_m2m_device(mdp);

> +	component_unbind_all(dev, mdp);

> +	mtk_mdp_unregister_component(mdp, &mdp->comp_self);

> +}

> +

> +static const struct component_master_ops mtk_mdp_com_ops = {

> +	.bind		= mtk_mdp_master_bind,

> +	.unbind		= mtk_mdp_master_unbind,

> +};

> +

>  void mtk_mdp_register_component(struct mtk_mdp_dev *mdp,

>  				struct mtk_mdp_comp *comp)

>  {

> @@ -108,8 +164,8 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  	struct mtk_mdp_dev *mdp;

>  	struct device *dev = &pdev->dev;

>  	struct device_node *node, *parent;

> -	struct mtk_mdp_comp *comp, *comp_temp;

> -	int ret = 0;

> +	int i, ret = 0;

> +	struct component_match *match = NULL;

>  

>  	mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);

>  	if (!mdp)

> @@ -134,36 +190,43 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  	}

>  

>  	/* Iterate over sibling MDP function blocks */

> +	i = 0;

>  	for_each_child_of_node(parent, node) {

> -		const struct of_device_id *of_id;

> -		enum mtk_mdp_comp_type comp_type;

> +		struct platform_device *pdev;

>  

> -		of_id = of_match_node(mtk_mdp_comp_dt_ids, node);

> -		if (!of_id)

> +		if (!of_match_node(mtk_mdp_comp_dt_ids, node))

>  			continue;

>  

> -		if (!of_device_is_available(node)) {

> -			dev_err(dev, "Skipping disabled component %pOF\n",

> -				node);

> +		if (!of_device_is_available(node))

>  			continue;

> -		}

>  

> -		comp_type = (enum mtk_mdp_comp_type)of_id->data;

> -

> -		comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);

> -		if (!comp) {

> -			ret = -ENOMEM;

> -			of_node_put(node);

> -			goto err_comp;

> +		pdev = of_find_device_by_node(node);

> +		if (!pdev) {

> +			dev_warn(dev, "Unable to find comp device %s\n",

> +				 node->full_name);


nit: Now that the limit is 100 chars, this can be in one line.

> +			continue;

>  		}

>  

> -		ret = mtk_mdp_comp_init(dev, node, comp, comp_type);

> -		if (ret) {

> -			of_node_put(node);

> -			goto err_comp;

> +		/*

> +		 * Do not add a match for my own (rdma0) device node.

> +		 * I will be managing it directly instead using comp_self.

> +		 */

> +		if (&pdev->dev != dev) {

> +			dev_dbg(dev, "adding match %d for: %pOF\n", i++, node);

> +			component_match_add_release(dev, &match, release_of,

> +						    compare_of,

> +						    of_node_get(node));

>  		}

> +	}

>  

> -		mtk_mdp_register_component(mdp, comp);

> +	/*

> +	 * Create a component for myself so that clocks can be toggled in

> +	 * clock_on().

> +	 */

> +	ret = mtk_mdp_comp_init(&mdp->comp_self, dev);

> +	if (ret) {

> +		dev_err(dev, "Failed to initialize component\n");

> +		goto err_comp;

>  	}

>  

>  	mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME);

> @@ -188,18 +251,12 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  		goto err_dev_register;

>  	}

>  

> -	ret = mtk_mdp_register_m2m_device(mdp);

> -	if (ret) {

> -		v4l2_err(&mdp->v4l2_dev, "Failed to init mem2mem device\n");

> -		goto err_m2m_register;

> -	}

> -

>  	mdp->vpu_dev = vpu_get_plat_device(pdev);

>  	ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp,

>  				  VPU_RST_MDP);

>  	if (ret) {

>  		dev_err(&pdev->dev, "Failed to register reset handler\n");

> -		goto err_m2m_register;

> +		goto err_wdt_reg;

>  	}

>  

>  	platform_set_drvdata(pdev, mdp);

> @@ -207,15 +264,25 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  	ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));

>  	if (ret) {

>  		dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n");

> -		goto err_m2m_register;

> +		goto err_set_max_seg_size;

> +	}

> +

> +	ret = component_master_add_with_match(dev, &mtk_mdp_com_ops, match);

> +	if (ret) {

> +		dev_err(dev, "Component master add failed\n");

> +		goto err_component_master_add;

>  	}

>  

> -	pm_runtime_enable(dev);

>  	dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id);

>  

>  	return 0;

>  

> -err_m2m_register:

> +err_component_master_add:

> +	vb2_dma_contig_clear_max_seg_size(&pdev->dev);

> +

> +err_set_max_seg_size:

> +

> +err_wdt_reg:

>  	v4l2_device_unregister(&mdp->v4l2_dev);

>  

>  err_dev_register:

> @@ -227,11 +294,6 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  err_alloc_job_wq:

>  

>  err_comp:

> -	list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {

> -		mtk_mdp_unregister_component(mdp, comp);

> -		mtk_mdp_comp_deinit(dev, comp);

> -	}

> -

>  	dev_dbg(dev, "err %d\n", ret);

>  	return ret;

>  }

> @@ -239,11 +301,10 @@ static int mtk_mdp_probe(struct platform_device *pdev)

>  static int mtk_mdp_remove(struct platform_device *pdev)

>  {

>  	struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev);

> -	struct mtk_mdp_comp *comp, *comp_temp;

>  

> -	pm_runtime_disable(&pdev->dev);

> +	component_master_del(&pdev->dev, &mtk_mdp_com_ops);

> +

>  	vb2_dma_contig_clear_max_seg_size(&pdev->dev);

> -	mtk_mdp_unregister_m2m_device(mdp);

>  	v4l2_device_unregister(&mdp->v4l2_dev);

>  

>  	flush_workqueue(mdp->wdt_wq);

> @@ -252,10 +313,8 @@ static int mtk_mdp_remove(struct platform_device *pdev)

>  	flush_workqueue(mdp->job_wq);

>  	destroy_workqueue(mdp->job_wq);

>  

> -	list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {

> -		mtk_mdp_unregister_component(mdp, comp);

> -		mtk_mdp_comp_deinit(&pdev->dev, comp);

> -	}

> +	if (!list_empty(&mdp->comp_list))

> +		dev_err(&pdev->dev, "not all components removed\n");


I'm not sure about media, but in most subsystems is not allowed to print and
error and do not return an error. So you should return and error here. Maybe
setup a warning as this is unlikely to happen?

>  

>  	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);

>  	return 0;

> @@ -310,7 +369,25 @@ static struct platform_driver mtk_mdp_driver = {

>  	}

>  };

>  

> -module_platform_driver(mtk_mdp_driver);

> +static struct platform_driver * const mtk_mdp_drivers[] = {

> +	&mtk_mdp_driver,

> +	&mtk_mdp_component_driver,

> +};

> +

> +static int __init mtk_mdp_init(void)

> +{

> +	return platform_register_drivers(mtk_mdp_drivers,

> +					 ARRAY_SIZE(mtk_mdp_drivers));

> +}

> +

> +static void __exit mtk_mdp_exit(void)

> +{

> +	platform_unregister_drivers(mtk_mdp_drivers,

> +				    ARRAY_SIZE(mtk_mdp_drivers));

> +}

> +

> +module_init(mtk_mdp_init);

> +module_exit(mtk_mdp_exit);

>  

>  MODULE_AUTHOR("Houlong Wei <houlong.wei@mediatek.com>");

>  MODULE_DESCRIPTION("Mediatek image processor driver");


I know this is not related to this patch, but is this driver really a "Mediatek
Image Processor driver"? When I read this I understand it is a ISP.

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h

> index a6e6dc36307b..8a52539b15d4 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h

> @@ -155,6 +155,7 @@ struct mtk_mdp_dev {

>  	struct mtk_mdp_variant		*variant;

>  	u16				id;

>  	struct list_head		comp_list;

> +	struct mtk_mdp_comp		comp_self;

>  	struct v4l2_m2m_dev		*m2m_dev;

>  	struct list_head		ctx_list;

>  	struct video_device		*vdev;

>
Enric Balletbo i Serra July 12, 2021, 10:12 a.m. UTC | #2
Hi Eizan,

Thank you for your patch.

On 9/7/21 4:23, Eizan Miyamoto wrote:
> The original intent of commit 86698b9505bbc ("media: mtk-mdp: convert

> mtk_mdp_dev.comp array to list") was to create a list to track all the

> MDP components that needed to have their clocks enabled/disabled when

> calling mtk_mdp_comp_clock_on/off. However, there was a bug inside

> mtk_mdp_register_component where the args to a call to list_add were

> swapped. The result is that only one component was added to

> mtk_mdp_dev.comp_list because comp_list was instead being

> repeatedly added to the single element lists headed by each

> mtk_mdp_comp.

> 

> The order of the args to list_add in mtk_mdp_register_component was

> fixed in https://patchwork.kernel.org/patch/11742895/ (Fix Null pointer

> dereference when calling list_add).

> 

> Then, as a result of https://patchwork.kernel.org/patch/11530769/

> (mtk-mdp: use pm_runtime in MDP component driver) if all the components

> are added to the component list, the mdp "master" / rdma0 component

> ends up having pm_runtime_get_sync() called on it twice recursively:

> 

>     rpm_resume+0x694/0x8f8

>     __pm_runtime_resume+0x7c/0xa0 ***NOTE***

>     mtk_mdp_comp_clock_on+0x48/0x104 [mtk_mdp]

>     mtk_mdp_pm_resume+0x2c/0x44 [mtk_mdp]

>     pm_generic_runtime_resume+0x34/0x48

>     __genpd_runtime_resume+0x6c/0x80

>     genpd_runtime_resume+0x104/0x1ac

>     __rpm_callback+0x120/0x238

>     rpm_callback+0x34/0x8c

>     rpm_resume+0x7a0/0x8f8

>     __pm_runtime_resume+0x7c/0xa0 ***NOTE***

>     mtk_mdp_m2m_start_streaming+0x2c/0x3c [mtk_mdp]

> 

> (The calls to pm_runtime_get_sync are inlined and correspond to the

> calls to __pm_runtime_resume). It is not correct to have

> pm_runtime_get_sync called recursively and the second call will block

> indefinitely.

> 

> As a result of all that, this change factors mtk_mdp_comp_clock_on/off

> into mtk_mdp_comp_power_on/off and moves the calls to

> pm_runtime_get/put into the power_on/off functions.

> 

> This change then special-cases the master/rdma0 MDP component and does

> these things:

> - the master/rdma0 component is not added to mtk_mdp_dev.comp_list

> - the master/rdma0 component has its clocks (*but not power*) toggled

>   by mtk_mpd_comp_clock_on/off inside mtk_mdp_clock_on/off.

> - the other components have their clocks *and* power toggled with

>   mtk_mdp_comp_power_on/off.

> 

> This change introduces the assumption that mtk_mdp_pm_resume will

> always be called though a callback from pm_runtime_get_sync made on the

> master / rdma0 component.

> 

> Signed-off-by: Eizan Miyamoto <eizan@chromium.org>

> ---

> 

> (no changes since v1)

> 

>  drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 28 ++++++++++++++-----

>  drivers/media/platform/mtk-mdp/mtk_mdp_comp.h |  3 ++

>  drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 23 ++++++++++-----

>  3 files changed, 40 insertions(+), 14 deletions(-)

> 

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> index 8b6158379f41..ce54863c24ce 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> @@ -53,9 +53,9 @@ static const struct of_device_id mtk_mdp_comp_driver_dt_match[] = {

>  };

>  MODULE_DEVICE_TABLE(of, mtk_mdp_comp_driver_dt_match);

>  

> -void mtk_mdp_comp_clock_on(struct mtk_mdp_comp *comp)

> +void mtk_mdp_comp_power_on(struct mtk_mdp_comp *comp)

>  {

> -	int i, err;

> +	int err;

>  

>  	if (comp->larb_dev) {

>  		err = mtk_smi_larb_get(comp->larb_dev);

> @@ -66,9 +66,25 @@ void mtk_mdp_comp_clock_on(struct mtk_mdp_comp *comp)

>  

>  	err = pm_runtime_get_sync(comp->dev);

>  	if (err < 0)

> -		dev_err(comp->dev,

> -			"failed to runtime get, err %d.\n",

> -			err);

> +		dev_err(comp->dev, "failed to runtime get, err %d.\n", err);


You can do that change in the previous patch which is the one that introduces
this dev_err call.

> +

> +	mtk_mdp_comp_clock_on(comp);

> +}

> +

> +void mtk_mdp_comp_power_off(struct mtk_mdp_comp *comp)

> +{

> +	int err;

> +

> +	mtk_mdp_comp_clock_off(comp);

> +

> +	err = pm_runtime_put_sync(comp->dev);

> +	if (err < 0)

> +		dev_err(comp->dev, "failed to runtime put, err %d.\n", err);

> +}

> +

> +void mtk_mdp_comp_clock_on(struct mtk_mdp_comp *comp)

> +{

> +	int i, err;

>  

>  	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {

>  		if (IS_ERR(comp->clk[i]))

> @@ -91,8 +107,6 @@ void mtk_mdp_comp_clock_off(struct mtk_mdp_comp *comp)

>  

>  	if (comp->larb_dev)

>  		mtk_smi_larb_put(comp->larb_dev);

> -

> -	pm_runtime_put_sync(comp->dev);


Ok, now that you are changing these functions I think it will be good to rework
these clock_on and off properly. First returning an error when is needed and
handling it properly on the error path instead of ignoring.

>  }

>  

>  static int mtk_mdp_comp_bind(struct device *dev, struct device *master,

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> index 5e2ee94a1b37..15ef9539a5a7 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h

> @@ -23,6 +23,9 @@ struct mtk_mdp_comp {

>  

>  int mtk_mdp_comp_init(struct mtk_mdp_comp *comp, struct device *dev);

>  

> +void mtk_mdp_comp_power_on(struct mtk_mdp_comp *comp);

> +void mtk_mdp_comp_power_off(struct mtk_mdp_comp *comp);

> +

>  void mtk_mdp_comp_clock_on(struct mtk_mdp_comp *comp);

>  void mtk_mdp_comp_clock_off(struct mtk_mdp_comp *comp);

>  

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> index 78c40a61df1d..3558a6587f51 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> @@ -54,8 +54,15 @@ static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp)

>  {

>  	struct mtk_mdp_comp *comp_node;

>  

> +	/*

> +	 * The master / rdma0 component will have pm_runtime_get_sync called

> +	 * on it through mtk_mdp_m2m_start_streaming, making it unnecessary to

> +	 * have mtk_mdp_comp_power_on called on it.

> +	 */

> +	mtk_mdp_comp_clock_on(&mdp->comp_self);

> +

>  	list_for_each_entry(comp_node, &mdp->comp_list, node)

> -		mtk_mdp_comp_clock_on(comp_node);

> +		mtk_mdp_comp_power_on(comp_node);

>  }

>  

>  static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp)

> @@ -63,7 +70,14 @@ static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp)

>  	struct mtk_mdp_comp *comp_node;

>  

>  	list_for_each_entry(comp_node, &mdp->comp_list, node)

> -		mtk_mdp_comp_clock_off(comp_node);

> +		mtk_mdp_comp_power_off(comp_node);

> +

> +	/*

> +	 * The master / rdma0 component will have pm_runtime_put called

> +	 * on it through mtk_mdp_m2m_stop_streaming, making it unnecessary to

> +	 * have mtk_mdp_comp_power_off called on it.

> +	 */

> +	mtk_mdp_comp_clock_off(&mdp->comp_self);

>  }

>  

>  static void mtk_mdp_wdt_worker(struct work_struct *work)

> @@ -102,8 +116,6 @@ static int mtk_mdp_master_bind(struct device *dev)

>  	int status;

>  	struct mtk_mdp_dev *mdp = dev_get_drvdata(dev);

>  

> -	mtk_mdp_register_component(mdp, &mdp->comp_self);

> -

>  	status = component_bind_all(dev, mdp);

>  	if (status) {

>  		dev_err(dev, "Failed to bind all components: %d\n", status);

> @@ -125,8 +137,6 @@ static int mtk_mdp_master_bind(struct device *dev)

>  	component_unbind_all(dev, mdp);

>  

>  err_component_bind_all:

> -	mtk_mdp_unregister_component(mdp, &mdp->comp_self);

> -

>  	return status;

>  }

>  

> @@ -137,7 +147,6 @@ static void mtk_mdp_master_unbind(struct device *dev)

>  	pm_runtime_disable(dev);

>  	mtk_mdp_unregister_m2m_device(mdp);

>  	component_unbind_all(dev, mdp);

> -	mtk_mdp_unregister_component(mdp, &mdp->comp_self);

>  }

>  

>  static const struct component_master_ops mtk_mdp_com_ops = {

>
Enric Balletbo i Serra July 12, 2021, 10:12 a.m. UTC | #3
Hi Eizan,

Thank you for your patch.

On 9/7/21 4:23, Eizan Miyamoto wrote:
> ... Instead of depending on the presence of a mediatek,vpu property in

> the device node.

> 

> That property was originally added to link to the vpu node so that the

> mtk_mdp_core driver could pass the right device to

> vpu_wdt_reg_handler(). However in a previous patch in this series,

> the driver has been modified to search the device tree for that node

> instead.

> 

> That property was also used to indicate the primary MDP device, so that

> it can be passed to the V4L2 subsystem as well as register it to be

> used when setting up queues in the open() callback for the filesystem

> device node that is created. In this case, assuming that the primary

> MDP device is the one with a specific alias seems useable because the

> alternative is to add a property to the device tree which doesn't

> actually represent any facet of hardware (i.e., this being the primary

> MDP device is a software decision). In other words, this solution is

> equally as arbitrary, but at least it doesn't add a property to a

> device node where said property is unrelated to the hardware present.

> 

> Signed-off-by: Eizan Miyamoto <eizan@chromium.org>

> ---

> 

> (no changes since v1)

> 

>  drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 47 ++++++++++++++-----

>  drivers/media/platform/mtk-mdp/mtk_mdp_core.c |  6 +++

>  2 files changed, 42 insertions(+), 11 deletions(-)

> 

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> index 87e3f72ff02b..de2d425efdd1 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c

> @@ -151,22 +151,48 @@ void mtk_mdp_comp_clock_off(struct mtk_mdp_comp *comp)

>  		mtk_smi_larb_put(comp->larb_dev);

>  }

>  

> +/*

> + * The MDP master device node is identified by the device tree alias

> + * "mdp-rdma0".

> + */

> +static int is_mdp_master(struct device *dev)


bool ? (and return true/false)

> +{

> +	struct device_node *aliases, *mdp_rdma0_node;

> +	const char *mdp_rdma0_path;

> +

> +	if (!dev->of_node)

> +		return 0;

> +

> +	aliases = of_find_node_by_path("/aliases");

> +	if (!aliases) {

> +		dev_err(dev, "no aliases found for mdp-rdma0");

> +		return 0;

> +	}

> +

> +	mdp_rdma0_path = of_get_property(aliases, "mdp-rdma0", NULL);

> +	if (!mdp_rdma0_path) {

> +		dev_err(dev, "get mdp-rdma0 property of /aliases failed");

> +		return 0;

> +	}

> +

> +	mdp_rdma0_node = of_find_node_by_path(mdp_rdma0_path);

> +	if (!mdp_rdma0_node) {

> +		dev_err(dev, "path resolution failed for %s", mdp_rdma0_path);

> +		return 0;

> +	}

> +

> +	return dev->of_node == mdp_rdma0_node;

> +}

> +

>  static int mtk_mdp_comp_bind(struct device *dev, struct device *master,

>  			void *data)

>  {

>  	struct mtk_mdp_comp *comp = dev_get_drvdata(dev);

>  	struct mtk_mdp_dev *mdp = data;

> -	struct device_node *vpu_node;

>  

>  	mtk_mdp_register_component(mdp, comp);

>  

> -	/*

> -	 * If this component has a "mediatek-vpu" property, it is responsible for

> -	 * notifying the mdp master driver about it so it can be further initialized

> -	 * later.

> -	 */

> -	vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0);

> -	if (vpu_node) {

> +	if (is_mdp_master(dev)) {

>  		int ret;

>  

>  		ret = v4l2_device_register(dev, &mdp->v4l2_dev);

> @@ -182,9 +208,8 @@ static int mtk_mdp_comp_bind(struct device *dev, struct device *master,

>  		}

>  

>  		/*

> -		 * presence of the "mediatek,vpu" property in a device node

> -		 * indicates that it is the primary MDP rdma device and MDP DMA

> -		 * ops should be handled by its DMA callbacks.

> +		 * MDP DMA ops will be handled by the DMA callbacks associated with this

> +		 * device;

>  		 */

>  		mdp->rdma_dev = dev;

>  	}

> diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> index b45d588d2659..e1fb39231248 100644

> --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c

> @@ -158,6 +158,12 @@ static int mtk_mdp_master_bind(struct device *dev)

>  		goto err_component_bind_all;

>  	}

>  

> +	if (mdp->rdma_dev == NULL) {

> +		dev_err(dev, "Primary MDP device not found");

> +		status = -ENODEV;

> +		goto err_component_bind_all;

> +	}

> +

>  	vpu_node = of_find_node_by_name(NULL, "vpu");

>  	if (!vpu_node) {

>  		dev_err(dev, "unable to find vpu node");

>