Commit ecd9acbf authored by Sylwester Nawrocki's avatar Sylwester Nawrocki Committed by Mauro Carvalho Chehab

[media] s5p-fimc: Handle sub-device interdependencies using deferred probing

In this driver there are several entities associated with separate
platform or I2C client devices, which may get probed in random order.

When the platform device bound to the media device driver is probed
all other entity drivers need to be already in place and initialized.
If any of them is not, fail the media device probe and return an error
indicating we need to be retried once any new driver gets registered.
The media device driver probe will not succeed until there are available
all needed sub-drivers, as specified in the platform data.

While at it, make sure the s5p-csis module (MIPI-CSI receiver driver)
does not get unloaded when in use, by guarding its usage with
try_module_get/module_put.

This patch is a prerequisite for adding the device tree support.
It now also allows again to unbind/bind the driver at runtime from
user space via sysfs.
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 6ec0163b
...@@ -1992,7 +1992,7 @@ static struct platform_driver fimc_driver = { ...@@ -1992,7 +1992,7 @@ static struct platform_driver fimc_driver = {
int __init fimc_register_driver(void) int __init fimc_register_driver(void)
{ {
return platform_driver_probe(&fimc_driver, fimc_probe); return platform_driver_register(&fimc_driver);
} }
void __exit fimc_unregister_driver(void) void __exit fimc_unregister_driver(void)
......
...@@ -214,14 +214,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, ...@@ -214,14 +214,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
return NULL; return NULL;
adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
if (!adapter) if (!adapter) {
return NULL; v4l2_warn(&fmd->v4l2_dev,
"Failed to get I2C adapter %d, deferring probe\n",
s_info->pdata->i2c_bus_num);
return ERR_PTR(-EPROBE_DEFER);
}
sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
s_info->pdata->board_info, NULL); s_info->pdata->board_info, NULL);
if (IS_ERR_OR_NULL(sd)) { if (IS_ERR_OR_NULL(sd)) {
i2c_put_adapter(adapter); i2c_put_adapter(adapter);
v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); v4l2_warn(&fmd->v4l2_dev,
return NULL; "Failed to acquire subdev %s, deferring probe\n",
s_info->pdata->board_info->type);
return ERR_PTR(-EPROBE_DEFER);
} }
v4l2_set_subdev_hostdata(sd, s_info); v4l2_set_subdev_hostdata(sd, s_info);
sd->grp_id = SENSOR_GROUP_ID; sd->grp_id = SENSOR_GROUP_ID;
...@@ -269,13 +275,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) ...@@ -269,13 +275,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
fmd->num_sensors = num_clients; fmd->num_sensors = num_clients;
for (i = 0; i < num_clients; i++) { for (i = 0; i < num_clients; i++) {
struct v4l2_subdev *sd;
fmd->sensor[i].pdata = &pdata->isp_info[i]; fmd->sensor[i].pdata = &pdata->isp_info[i];
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
if (ret) if (ret)
break; break;
fmd->sensor[i].subdev = sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
fimc_md_register_sensor(fmd, &fmd->sensor[i]);
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
if (!IS_ERR(sd)) {
fmd->sensor[i].subdev = sd;
} else {
fmd->sensor[i].subdev = NULL;
ret = PTR_ERR(sd);
break;
}
if (ret) if (ret)
break; break;
} }
...@@ -336,22 +351,45 @@ static int csis_register_callback(struct device *dev, void *p) ...@@ -336,22 +351,45 @@ static int csis_register_callback(struct device *dev, void *p)
*/ */
static int fimc_md_register_platform_entities(struct fimc_md *fmd) static int fimc_md_register_platform_entities(struct fimc_md *fmd)
{ {
struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
struct device_driver *driver; struct device_driver *driver;
int ret; int ret, i;
driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
if (!driver) if (!driver) {
return -ENODEV; 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, ret = driver_for_each_device(driver, NULL, fmd,
fimc_register_callback); fimc_register_callback);
if (ret) if (ret)
return ret; return ret;
/*
* Check if there is any sensor on the MIPI-CSI2 bus and
* if not skip the s5p-csis module loading.
*/
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); driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
if (driver) if (!driver || !try_module_get(driver->owner)) {
ret = driver_for_each_device(driver, NULL, fmd, v4l2_warn(&fmd->v4l2_dev,
csis_register_callback); "%s driver not found, deffering probe\n",
return ret; CSIS_DRIVER_NAME);
return -EPROBE_DEFER;
}
return driver_for_each_device(driver, NULL, fmd,
csis_register_callback);
} }
static void fimc_md_unregister_entities(struct fimc_md *fmd) static void fimc_md_unregister_entities(struct fimc_md *fmd)
...@@ -369,6 +407,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) ...@@ -369,6 +407,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
if (fmd->csis[i].sd == NULL) if (fmd->csis[i].sd == NULL)
continue; continue;
v4l2_device_unregister_subdev(fmd->csis[i].sd); v4l2_device_unregister_subdev(fmd->csis[i].sd);
module_put(fmd->csis[i].sd->owner);
fmd->csis[i].sd = NULL; fmd->csis[i].sd = NULL;
} }
for (i = 0; i < fmd->num_sensors; i++) { for (i = 0; i < fmd->num_sensors; i++) {
...@@ -744,7 +783,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, ...@@ -744,7 +783,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev,
static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
fimc_md_sysfs_show, fimc_md_sysfs_store); fimc_md_sysfs_show, fimc_md_sysfs_store);
static int __devinit fimc_md_probe(struct platform_device *pdev) static int fimc_md_probe(struct platform_device *pdev)
{ {
struct v4l2_device *v4l2_dev; struct v4l2_device *v4l2_dev;
struct fimc_md *fmd; struct fimc_md *fmd;
...@@ -841,10 +880,12 @@ static struct platform_driver fimc_md_driver = { ...@@ -841,10 +880,12 @@ static struct platform_driver fimc_md_driver = {
int __init fimc_md_init(void) int __init fimc_md_init(void)
{ {
int ret; int ret;
request_module("s5p-csis"); request_module("s5p-csis");
ret = fimc_register_driver(); ret = fimc_register_driver();
if (ret) if (ret)
return ret; return ret;
return platform_driver_register(&fimc_md_driver); return platform_driver_register(&fimc_md_driver);
} }
void __exit fimc_md_exit(void) void __exit fimc_md_exit(void)
......
...@@ -715,19 +715,8 @@ static struct platform_driver s5pcsis_driver = { ...@@ -715,19 +715,8 @@ static struct platform_driver s5pcsis_driver = {
}, },
}; };
static int __init s5pcsis_init(void) module_platform_driver(s5pcsis_driver);
{
return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe);
}
static void __exit s5pcsis_exit(void)
{
platform_driver_unregister(&s5pcsis_driver);
}
module_init(s5pcsis_init);
module_exit(s5pcsis_exit);
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment