mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-29 13:00:35 +00:00
[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: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
969e877cc1
commit
7b43a6f3f5
@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* S5P/EXYNOS4 SoC series camera host interface media device driver
|
* S5P/EXYNOS4 SoC series camera host interface media device driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
|
* Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
|
||||||
* Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
|
* Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published
|
* 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 v4l2_subdev *sd;
|
||||||
struct fimc_md *fmd = p;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS)
|
if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
|
||||||
return 0;
|
fmd->fimc_lite[fimc_lite->index]))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
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)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
|
||||||
|
{
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
sd = &fimc->vid_cap.subdev;
|
sd = &fimc->vid_cap.subdev;
|
||||||
sd->grp_id = GRP_ID_FIMC;
|
sd->grp_id = GRP_ID_FIMC;
|
||||||
v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
|
v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
|
||||||
|
|
||||||
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
|
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
|
||||||
if (ret) {
|
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",
|
v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
|
||||||
fimc->id, ret);
|
fimc->id, ret);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
fmd->fimc[fimc->id] = fimc;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fimc_lite_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 fimc_lite *fimc = dev_get_drvdata(dev);
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct fimc_md *fmd = p;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fimc->subdev.grp_id = GRP_ID_FLITE;
|
|
||||||
v4l2_set_subdev_hostdata(&fimc->subdev, (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmd->fimc_lite[fimc->index] = fimc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int csis_register_callback(struct device *dev, void *p)
|
|
||||||
{
|
|
||||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
||||||
struct platform_device *pdev;
|
|
||||||
struct fimc_md *fmd = p;
|
|
||||||
int id, ret;
|
int id, ret;
|
||||||
|
|
||||||
if (!sd)
|
id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id);
|
||||||
return 0;
|
|
||||||
pdev = v4l2_get_subdevdata(sd);
|
if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd))
|
||||||
if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
|
return -EBUSY;
|
||||||
return 0;
|
|
||||||
v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
|
if (WARN_ON(id >= CSIS_MAX_ENTITIES))
|
||||||
|
return 0;
|
||||||
|
|
||||||
id = pdev->id < 0 ? 0 : pdev->id;
|
|
||||||
sd->grp_id = GRP_ID_CSIS;
|
sd->grp_id = GRP_ID_CSIS;
|
||||||
|
|
||||||
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
|
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
fmd->csis[id].sd = sd;
|
fmd->csis[id].sd = sd;
|
||||||
else
|
else
|
||||||
v4l2_err(&fmd->v4l2_dev,
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int fimc_md_register_platform_entity(struct fimc_md *fmd,
|
||||||
* fimc_md_register_platform_entities - register FIMC and CSIS media entities
|
struct platform_device *pdev,
|
||||||
*/
|
int plat_entity)
|
||||||
static int fimc_md_register_platform_entities(struct fimc_md *fmd)
|
|
||||||
{
|
{
|
||||||
struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_driver *driver;
|
int ret = -EPROBE_DEFER;
|
||||||
int ret, i;
|
void *drvdata;
|
||||||
|
|
||||||
driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
|
/* Lock to ensure dev->driver won't change. */
|
||||||
if (!driver) {
|
device_lock(dev);
|
||||||
v4l2_warn(&fmd->v4l2_dev,
|
|
||||||
"%s driver not found, deffering probe\n",
|
|
||||||
FIMC_MODULE_NAME);
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = driver_for_each_device(driver, NULL, fmd,
|
if (!dev->driver || !try_module_get(dev->driver->owner))
|
||||||
fimc_register_callback);
|
goto dev_unlock;
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
|
drvdata = dev_get_drvdata(dev);
|
||||||
if (driver && try_module_get(driver->owner)) {
|
/* Some subdev didn't probe succesfully id drvdata is NULL */
|
||||||
ret = driver_for_each_device(driver, NULL, fmd,
|
if (drvdata) {
|
||||||
fimc_lite_register_callback);
|
switch (plat_entity) {
|
||||||
if (ret)
|
case IDX_FIMC:
|
||||||
return ret;
|
ret = register_fimc_entity(fmd, drvdata);
|
||||||
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;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ret)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
|
module_put(dev->driver->owner);
|
||||||
if (!driver || !try_module_get(driver->owner)) {
|
dev_unlock:
|
||||||
v4l2_warn(&fmd->v4l2_dev,
|
device_unlock(dev);
|
||||||
"%s driver not found, deffering probe\n",
|
if (ret == -EPROBE_DEFER)
|
||||||
CSIS_DRIVER_NAME);
|
dev_info(&fmd->pdev->dev, "deferring %s device registration\n",
|
||||||
return -EPROBE_DEFER;
|
dev_name(dev));
|
||||||
|
else if (ret < 0)
|
||||||
|
dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n",
|
||||||
|
dev_name(dev), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
if (plat_entity >= 0)
|
||||||
csis_register_callback);
|
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)
|
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);
|
fimc_md_unregister_sensor(fmd->sensor[i].subdev);
|
||||||
fmd->sensor[i].subdev = NULL;
|
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 */
|
/* Protect the media graph while we're registering entities */
|
||||||
mutex_lock(&fmd->media_dev.graph_mutex);
|
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)
|
if (ret)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user