Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b43a6f3 authored by Sylwester Nawrocki's avatar Sylwester Nawrocki Committed by Mauro Carvalho Chehab
Browse files

[media] s5p-fimc: Change platform subdevs registration method



The previous method of registering platform entities into the main
driver using driver_find() and then iterating over devices bound to
a driver was racy and is being removed here. Nothing was preventing
module from unloading during a call to try_module_get(driver->owner).
Instead, we look up a device first and then check for its driver while
holding device lock.
The platform sub-devices are looked up and registered to the top
level driver. When any sub-device is not yet initialized and ready
the main driver's probe() will be deferred.

Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 969e877c
Loading
Loading
Loading
Loading
+108 −95
Original line number Diff line number Diff line
/*
 * S5P/EXYNOS4 SoC series camera host interface media device driver
 *
 * Copyright (C) 2011 Samsung Electronics Co., Ltd.
 * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
 * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
 * Sylwester Nawrocki <s.nawrocki@samsung.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
@@ -312,138 +312,149 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
}

/*
 * MIPI CSIS and FIMC platform devices registration.
 * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.
 */
static int fimc_register_callback(struct device *dev, void *p)

static int register_fimc_lite_entity(struct fimc_md *fmd,
				     struct fimc_lite *fimc_lite)
{
	struct fimc_dev *fimc = dev_get_drvdata(dev);
	struct v4l2_subdev *sd;
	struct fimc_md *fmd = p;
	int ret;

	if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS)
		return 0;
	if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
		    fmd->fimc_lite[fimc_lite->index]))
		return -EBUSY;

	sd = &fimc->vid_cap.subdev;
	sd->grp_id = GRP_ID_FIMC;
	sd = &fimc_lite->subdev;
	sd->grp_id = GRP_ID_FLITE;
	v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);

	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
	if (ret) {
		v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
			 fimc->id, ret);
	if (!ret)
		fmd->fimc_lite[fimc_lite->index] = fimc_lite;
	else
		v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n",
			 fimc_lite->index);
	return ret;
}

	fmd->fimc[fimc->id] = fimc;
	return 0;
}

static int fimc_lite_register_callback(struct device *dev, void *p)
static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
{
	struct fimc_lite *fimc = dev_get_drvdata(dev);
	struct fimc_md *fmd = p;
	struct v4l2_subdev *sd;
	int ret;

	if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS)
		return 0;
	if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
		return -EBUSY;

	fimc->subdev.grp_id = GRP_ID_FLITE;
	v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops);
	sd = &fimc->vid_cap.subdev;
	sd->grp_id = GRP_ID_FIMC;
	v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);

	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev);
	if (ret) {
		v4l2_err(&fmd->v4l2_dev,
			 "Failed to register FIMC-LITE.%d (%d)\n",
			 fimc->index, ret);
		return ret;
	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
	if (!ret) {
		fmd->fimc[fimc->id] = fimc;
		fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
	} else {
		v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
			 fimc->id, ret);
	}

	fmd->fimc_lite[fimc->index] = fimc;
	return 0;
	return ret;
}

static int csis_register_callback(struct device *dev, void *p)
static int register_csis_entity(struct fimc_md *fmd,
				struct platform_device *pdev,
				struct v4l2_subdev *sd)
{
	struct v4l2_subdev *sd = dev_get_drvdata(dev);
	struct platform_device *pdev;
	struct fimc_md *fmd = p;
	struct device_node *node = pdev->dev.of_node;
	int id, ret;

	if (!sd)
		return 0;
	pdev = v4l2_get_subdevdata(sd);
	if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
	id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id);

	if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd))
		return -EBUSY;

	if (WARN_ON(id >= CSIS_MAX_ENTITIES))
		return 0;
	v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);

	id = pdev->id < 0 ? 0 : pdev->id;
	sd->grp_id = GRP_ID_CSIS;

	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
	if (!ret)
		fmd->csis[id].sd = sd;
	else
		v4l2_err(&fmd->v4l2_dev,
			 "Failed to register CSIS subdevice: %d\n", ret);
			 "Failed to register MIPI-CSIS.%d (%d)\n", id, ret);
	return ret;
}

/**
 * fimc_md_register_platform_entities - register FIMC and CSIS media entities
 */
static int fimc_md_register_platform_entities(struct fimc_md *fmd)
static int fimc_md_register_platform_entity(struct fimc_md *fmd,
					    struct platform_device *pdev,
					    int plat_entity)
{
	struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
	struct device_driver *driver;
	int ret, i;

	driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
	if (!driver) {
		v4l2_warn(&fmd->v4l2_dev,
			 "%s driver not found, deffering probe\n",
			 FIMC_MODULE_NAME);
		return -EPROBE_DEFER;
	struct device *dev = &pdev->dev;
	int ret = -EPROBE_DEFER;
	void *drvdata;

	/* Lock to ensure dev->driver won't change. */
	device_lock(dev);

	if (!dev->driver || !try_module_get(dev->driver->owner))
		goto dev_unlock;

	drvdata = dev_get_drvdata(dev);
	/* Some subdev didn't probe succesfully id drvdata is NULL */
	if (drvdata) {
		switch (plat_entity) {
		case IDX_FIMC:
			ret = register_fimc_entity(fmd, drvdata);
			break;
		case IDX_FLITE:
			ret = register_fimc_lite_entity(fmd, drvdata);
			break;
		case IDX_CSIS:
			ret = register_csis_entity(fmd, pdev, drvdata);
			break;
		default:
			ret = -ENODEV;
		}
	}

	ret = driver_for_each_device(driver, NULL, fmd,
				     fimc_register_callback);
	if (ret)
		return ret;

	driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
	if (driver && try_module_get(driver->owner)) {
		ret = driver_for_each_device(driver, NULL, fmd,
					     fimc_lite_register_callback);
		if (ret)
	module_put(dev->driver->owner);
dev_unlock:
	device_unlock(dev);
	if (ret == -EPROBE_DEFER)
		dev_info(&fmd->pdev->dev, "deferring %s device registration\n",
			dev_name(dev));
	else if (ret < 0)
		dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n",
			dev_name(dev), ret);
	return ret;
		module_put(driver->owner);
}
	/*
	 * Check if there is any sensor on the MIPI-CSI2 bus and
	 * if not skip the s5p-csis module loading.
	 */
	if (pdata == NULL)
		return 0;
	for (i = 0; i < pdata->num_clients; i++) {
		if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
			ret = 1;
			break;
		}
	}
	if (!ret)
		return 0;

	driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
	if (!driver || !try_module_get(driver->owner)) {
		v4l2_warn(&fmd->v4l2_dev,
			 "%s driver not found, deffering probe\n",
			 CSIS_DRIVER_NAME);
		return -EPROBE_DEFER;
static int fimc_md_pdev_match(struct device *dev, void *data)
{
	struct platform_device *pdev = to_platform_device(dev);
	int plat_entity = -1;
	int ret;
	char *p;

	if (!get_device(dev))
		return -ENODEV;

	if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) {
		plat_entity = IDX_CSIS;
	} else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) {
		plat_entity = IDX_FLITE;
	} else {
		p = strstr(pdev->name, "fimc");
		if (p && *(p + 4) == 0)
			plat_entity = IDX_FIMC;
	}

	return driver_for_each_device(driver, NULL, fmd,
				      csis_register_callback);
	if (plat_entity >= 0)
		ret = fimc_md_register_platform_entity(data, pdev,
						       plat_entity);
	put_device(dev);
	return 0;
}

static void fimc_md_unregister_entities(struct fimc_md *fmd)
@@ -477,6 +488,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
		fimc_md_unregister_sensor(fmd->sensor[i].subdev);
		fmd->sensor[i].subdev = NULL;
	}
	v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n");
}

/**
@@ -939,7 +951,8 @@ static int fimc_md_probe(struct platform_device *pdev)
	/* Protect the media graph while we're registering entities */
	mutex_lock(&fmd->media_dev.graph_mutex);

	ret = fimc_md_register_platform_entities(fmd);
	ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
					fimc_md_pdev_match);
	if (ret)
		goto err_unlock;