Commit d079f94c authored by Steve Longerbeam's avatar Steve Longerbeam Committed by Mauro Carvalho Chehab
Browse files

media: platform: Switch to v4l2_async_notifier_add_subdev



Switch all media platform drivers to call v4l2_async_notifier_add_subdev()
to add asd's to a notifier, in place of referencing the notifier->subdevs[]
array. These drivers also must now call v4l2_async_notifier_init() before
adding asd's to their notifiers.

There may still be cases where a platform driver maintains a list of
asd's that is a duplicate of the notifier asd_list, in which case its
possible the platform driver list can be removed, and can reference the
notifier asd_list instead. One example of where a duplicate list has
been removed in this patch is xilinx-vipp.c. If there are such cases
remaining, those drivers should be optimized to remove the duplicate
platform driver asd lists.

None of the changes to the platform drivers in this patch have been
tested. Verify that the async subdevices needed by the platform are
bound at load time, and that the driver unloads and reloads correctly
with no memory leaking of asd objects.

Suggested-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarSteve Longerbeam <slongerbeam@gmail.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent d5099f81
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1506,7 +1506,7 @@ static int cio2_notifier_init(struct cio2_device *cio2)
	if (ret < 0)
		return ret;

	if (!cio2->notifier.num_subdevs)
	if (list_empty(&cio2->notifier.asd_list))
		return -ENODEV;	/* no endpoint */

	cio2->notifier.ops = &cio2_async_ops;
+43 −39
Original line number Diff line number Diff line
@@ -2423,30 +2423,32 @@ static const struct v4l2_async_notifier_operations vpfe_async_ops = {
};

static struct vpfe_config *
vpfe_get_pdata(struct platform_device *pdev)
vpfe_get_pdata(struct vpfe_device *vpfe)
{
	struct device_node *endpoint = NULL;
	struct v4l2_fwnode_endpoint bus_cfg;
	struct device *dev = vpfe->pdev;
	struct vpfe_subdev_info *sdinfo;
	struct vpfe_config *pdata;
	unsigned int flags;
	unsigned int i;
	int err;

	dev_dbg(&pdev->dev, "vpfe_get_pdata\n");
	dev_dbg(dev, "vpfe_get_pdata\n");

	if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
		return pdev->dev.platform_data;
	v4l2_async_notifier_init(&vpfe->notifier);

	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if (!IS_ENABLED(CONFIG_OF) || !dev->of_node)
		return dev->platform_data;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return NULL;

	for (i = 0; ; i++) {
		struct device_node *rem;

		endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
						      endpoint);
		endpoint = of_graph_get_next_endpoint(dev->of_node, endpoint);
		if (!endpoint)
			break;

@@ -2474,16 +2476,16 @@ vpfe_get_pdata(struct platform_device *pdev)
		err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
						 &bus_cfg);
		if (err) {
			dev_err(&pdev->dev, "Could not parse the endpoint\n");
			goto done;
			dev_err(dev, "Could not parse the endpoint\n");
			goto cleanup;
		}

		sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width;

		if (sdinfo->vpfe_param.bus_width < 8 ||
			sdinfo->vpfe_param.bus_width > 16) {
			dev_err(&pdev->dev, "Invalid bus width.\n");
			goto done;
			dev_err(dev, "Invalid bus width.\n");
			goto cleanup;
		}

		flags = bus_cfg.bus.parallel.flags;
@@ -2496,29 +2498,25 @@ vpfe_get_pdata(struct platform_device *pdev)

		rem = of_graph_get_remote_port_parent(endpoint);
		if (!rem) {
			dev_err(&pdev->dev, "Remote device at %pOF not found\n",
			dev_err(dev, "Remote device at %pOF not found\n",
				endpoint);
			goto done;
			goto cleanup;
		}

		pdata->asd[i] = devm_kzalloc(&pdev->dev,
					     sizeof(struct v4l2_async_subdev),
					     GFP_KERNEL);
		if (!pdata->asd[i]) {
		pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev(
			&vpfe->notifier, of_fwnode_handle(rem),
			sizeof(struct v4l2_async_subdev));
		if (IS_ERR(pdata->asd[i])) {
			of_node_put(rem);
			pdata = NULL;
			goto done;
			goto cleanup;
		}

		pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_FWNODE;
		pdata->asd[i]->match.fwnode = of_fwnode_handle(rem);
		of_node_put(rem);
	}

	of_node_put(endpoint);
	return pdata;

done:
cleanup:
	v4l2_async_notifier_cleanup(&vpfe->notifier);
	of_node_put(endpoint);
	return NULL;
}
@@ -2530,34 +2528,39 @@ done:
 */
static int vpfe_probe(struct platform_device *pdev)
{
	struct vpfe_config *vpfe_cfg = vpfe_get_pdata(pdev);
	struct vpfe_config *vpfe_cfg;
	struct vpfe_device *vpfe;
	struct vpfe_ccdc *ccdc;
	struct resource	*res;
	int ret;

	if (!vpfe_cfg) {
		dev_err(&pdev->dev, "No platform data\n");
		return -EINVAL;
	}

	vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL);
	if (!vpfe)
		return -ENOMEM;

	vpfe->pdev = &pdev->dev;

	vpfe_cfg = vpfe_get_pdata(vpfe);
	if (!vpfe_cfg) {
		dev_err(&pdev->dev, "No platform data\n");
		return -EINVAL;
	}

	vpfe->cfg = vpfe_cfg;
	ccdc = &vpfe->ccdc;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(ccdc->ccdc_cfg.base_addr))
		return PTR_ERR(ccdc->ccdc_cfg.base_addr);
	if (IS_ERR(ccdc->ccdc_cfg.base_addr)) {
		ret = PTR_ERR(ccdc->ccdc_cfg.base_addr);
		goto probe_out_cleanup;
	}

	ret = platform_get_irq(pdev, 0);
	if (ret <= 0) {
		dev_err(&pdev->dev, "No IRQ resource\n");
		return -ENODEV;
		ret = -ENODEV;
		goto probe_out_cleanup;
	}
	vpfe->irq = ret;

@@ -2565,14 +2568,15 @@ static int vpfe_probe(struct platform_device *pdev)
			       "vpfe_capture0", vpfe);
	if (ret) {
		dev_err(&pdev->dev, "Unable to request interrupt\n");
		return -EINVAL;
		ret = -EINVAL;
		goto probe_out_cleanup;
	}

	ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev);
	if (ret) {
		vpfe_err(vpfe,
			"Unable to register v4l2 device.\n");
		return ret;
		goto probe_out_cleanup;
	}

	/* set the driver data in platform device */
@@ -2596,11 +2600,8 @@ static int vpfe_probe(struct platform_device *pdev)
		goto probe_out_v4l2_unregister;
	}

	vpfe->notifier.subdevs = vpfe->cfg->asd;
	vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd);
	vpfe->notifier.ops = &vpfe_async_ops;
	ret = v4l2_async_notifier_register(&vpfe->v4l2_dev,
						&vpfe->notifier);
	ret = v4l2_async_notifier_register(&vpfe->v4l2_dev, &vpfe->notifier);
	if (ret) {
		vpfe_err(vpfe, "Error registering async notifier\n");
		ret = -EINVAL;
@@ -2611,6 +2612,8 @@ static int vpfe_probe(struct platform_device *pdev)

probe_out_v4l2_unregister:
	v4l2_device_unregister(&vpfe->v4l2_dev);
probe_out_cleanup:
	v4l2_async_notifier_cleanup(&vpfe->notifier);
	return ret;
}

@@ -2626,6 +2629,7 @@ static int vpfe_remove(struct platform_device *pdev)
	pm_runtime_disable(&pdev->dev);

	v4l2_async_notifier_unregister(&vpfe->notifier);
	v4l2_async_notifier_cleanup(&vpfe->notifier);
	v4l2_device_unregister(&vpfe->v4l2_dev);
	video_unregister_device(&vpfe->video_dev);

+12 −3
Original line number Diff line number Diff line
@@ -1983,8 +1983,10 @@ static void isc_subdev_cleanup(struct isc_device *isc)
{
	struct isc_subdev_entity *subdev_entity;

	list_for_each_entry(subdev_entity, &isc->subdev_entities, list)
	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
		v4l2_async_notifier_unregister(&subdev_entity->notifier);
		v4l2_async_notifier_cleanup(&subdev_entity->notifier);
	}

	INIT_LIST_HEAD(&isc->subdev_entities);
}
@@ -2201,8 +2203,15 @@ static int atmel_isc_probe(struct platform_device *pdev)
	}

	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
		subdev_entity->notifier.subdevs = &subdev_entity->asd;
		subdev_entity->notifier.num_subdevs = 1;
		v4l2_async_notifier_init(&subdev_entity->notifier);

		ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier,
						     subdev_entity->asd);
		if (ret) {
			fwnode_handle_put(subdev_entity->asd->match.fwnode);
			goto cleanup_subdev;
		}

		subdev_entity->notifier.ops = &isc_async_ops;

		ret = v4l2_async_notifier_register(&isc->v4l2_dev,
+7 −10
Original line number Diff line number Diff line
@@ -1124,7 +1124,6 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node)

static int isi_graph_init(struct atmel_isi *isi)
{
	struct v4l2_async_subdev **subdevs = NULL;
	int ret;

	/* Parse the graph to extract a list of subdevice DT nodes. */
@@ -1134,23 +1133,20 @@ static int isi_graph_init(struct atmel_isi *isi)
		return ret;
	}

	/* Register the subdevices notifier. */
	subdevs = devm_kzalloc(isi->dev, sizeof(*subdevs), GFP_KERNEL);
	if (!subdevs) {
	v4l2_async_notifier_init(&isi->notifier);

	ret = v4l2_async_notifier_add_subdev(&isi->notifier, &isi->entity.asd);
	if (ret) {
		of_node_put(isi->entity.node);
		return -ENOMEM;
		return ret;
	}

	subdevs[0] = &isi->entity.asd;

	isi->notifier.subdevs = subdevs;
	isi->notifier.num_subdevs = 1;
	isi->notifier.ops = &isi_graph_notify_ops;

	ret = v4l2_async_notifier_register(&isi->v4l2_dev, &isi->notifier);
	if (ret < 0) {
		dev_err(isi->dev, "Notifier registration failed\n");
		of_node_put(isi->entity.node);
		v4l2_async_notifier_cleanup(&isi->notifier);
		return ret;
	}

@@ -1303,6 +1299,7 @@ static int atmel_isi_remove(struct platform_device *pdev)
			isi->fb_descriptors_phys);
	pm_runtime_disable(&pdev->dev);
	v4l2_async_notifier_unregister(&isi->notifier);
	v4l2_async_notifier_cleanup(&isi->notifier);
	v4l2_device_unregister(&isi->v4l2_dev);

	return 0;
+17 −11
Original line number Diff line number Diff line
@@ -399,18 +399,22 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
	csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
	of_node_put(ep);

	csi2rx->notifier.subdevs = devm_kzalloc(csi2rx->dev,
						sizeof(*csi2rx->notifier.subdevs),
						GFP_KERNEL);
	if (!csi2rx->notifier.subdevs)
		return -ENOMEM;
	v4l2_async_notifier_init(&csi2rx->notifier);

	ret = v4l2_async_notifier_add_subdev(&csi2rx->notifier, &csi2rx->asd);
	if (ret) {
		fwnode_handle_put(csi2rx->asd.match.fwnode);
		return ret;
	}

	csi2rx->notifier.subdevs[0] = &csi2rx->asd;
	csi2rx->notifier.num_subdevs = 1;
	csi2rx->notifier.ops = &csi2rx_notifier_ops;

	return v4l2_async_subdev_notifier_register(&csi2rx->subdev,
	ret = v4l2_async_subdev_notifier_register(&csi2rx->subdev,
						  &csi2rx->notifier);
	if (ret)
		v4l2_async_notifier_cleanup(&csi2rx->notifier);

	return ret;
}

static int csi2rx_probe(struct platform_device *pdev)
@@ -450,11 +454,11 @@ static int csi2rx_probe(struct platform_device *pdev)
	ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX,
				     csi2rx->pads);
	if (ret)
		goto err_free_priv;
		goto err_cleanup;

	ret = v4l2_async_register_subdev(&csi2rx->subdev);
	if (ret < 0)
		goto err_free_priv;
		goto err_cleanup;

	dev_info(&pdev->dev,
		 "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n",
@@ -463,6 +467,8 @@ static int csi2rx_probe(struct platform_device *pdev)

	return 0;

err_cleanup:
	v4l2_async_notifier_cleanup(&csi2rx->notifier);
err_free_priv:
	kfree(csi2rx);
	return ret;
Loading