aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2012-12-12 06:16:05 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-02-05 14:49:56 -0500
commit7b43a6f3f517109c2912d82f7f666a84420689bc (patch)
treed7313ea9607b53640209683ed15aaa1bfcd7f8db
parent969e877cc1e6e91dc76f973d08ad70e2065c56ae (diff)
[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>
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.c203
1 files changed, 108 insertions, 95 deletions
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
index 52e1aa3c8982..2b0587204868 100644
--- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
@@ -1,8 +1,8 @@
1/* 1/*
2 * S5P/EXYNOS4 SoC series camera host interface media device driver 2 * S5P/EXYNOS4 SoC series camera host interface media device driver
3 * 3 *
4 * Copyright (C) 2011 Samsung Electronics Co., Ltd. 4 * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
5 * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> 5 * Sylwester Nawrocki <s.nawrocki@samsung.com>
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published 8 * 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)
312} 312}
313 313
314/* 314/*
315 * MIPI CSIS and FIMC platform devices registration. 315 * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.
316 */ 316 */
317static int fimc_register_callback(struct device *dev, void *p) 317
318static int register_fimc_lite_entity(struct fimc_md *fmd,
319 struct fimc_lite *fimc_lite)
318{ 320{
319 struct fimc_dev *fimc = dev_get_drvdata(dev);
320 struct v4l2_subdev *sd; 321 struct v4l2_subdev *sd;
321 struct fimc_md *fmd = p;
322 int ret; 322 int ret;
323 323
324 if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS) 324 if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
325 return 0; 325 fmd->fimc_lite[fimc_lite->index]))
326 return -EBUSY;
326 327
327 sd = &fimc->vid_cap.subdev; 328 sd = &fimc_lite->subdev;
328 sd->grp_id = GRP_ID_FIMC; 329 sd->grp_id = GRP_ID_FLITE;
329 v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); 330 v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
330 331
331 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); 332 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
332 if (ret) { 333 if (!ret)
333 v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", 334 fmd->fimc_lite[fimc_lite->index] = fimc_lite;
334 fimc->id, ret); 335 else
335 return ret; 336 v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n",
336 } 337 fimc_lite->index);
337 338 return ret;
338 fmd->fimc[fimc->id] = fimc;
339 return 0;
340} 339}
341 340
342static int fimc_lite_register_callback(struct device *dev, void *p) 341static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
343{ 342{
344 struct fimc_lite *fimc = dev_get_drvdata(dev); 343 struct v4l2_subdev *sd;
345 struct fimc_md *fmd = p;
346 int ret; 344 int ret;
347 345
348 if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) 346 if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
349 return 0; 347 return -EBUSY;
350 348
351 fimc->subdev.grp_id = GRP_ID_FLITE; 349 sd = &fimc->vid_cap.subdev;
352 v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); 350 sd->grp_id = GRP_ID_FIMC;
351 v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
353 352
354 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); 353 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
355 if (ret) { 354 if (!ret) {
356 v4l2_err(&fmd->v4l2_dev, 355 fmd->fimc[fimc->id] = fimc;
357 "Failed to register FIMC-LITE.%d (%d)\n", 356 fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
358 fimc->index, ret); 357 } else {
359 return ret; 358 v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
359 fimc->id, ret);
360 } 360 }
361 361 return ret;
362 fmd->fimc_lite[fimc->index] = fimc;
363 return 0;
364} 362}
365 363
366static int csis_register_callback(struct device *dev, void *p) 364static int register_csis_entity(struct fimc_md *fmd,
365 struct platform_device *pdev,
366 struct v4l2_subdev *sd)
367{ 367{
368 struct v4l2_subdev *sd = dev_get_drvdata(dev); 368 struct device_node *node = pdev->dev.of_node;
369 struct platform_device *pdev;
370 struct fimc_md *fmd = p;
371 int id, ret; 369 int id, ret;
372 370
373 if (!sd) 371 id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id);
374 return 0; 372
375 pdev = v4l2_get_subdevdata(sd); 373 if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd))
376 if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES) 374 return -EBUSY;
375
376 if (WARN_ON(id >= CSIS_MAX_ENTITIES))
377 return 0; 377 return 0;
378 v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
379 378
380 id = pdev->id < 0 ? 0 : pdev->id;
381 sd->grp_id = GRP_ID_CSIS; 379 sd->grp_id = GRP_ID_CSIS;
382
383 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); 380 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
384 if (!ret) 381 if (!ret)
385 fmd->csis[id].sd = sd; 382 fmd->csis[id].sd = sd;
386 else 383 else
387 v4l2_err(&fmd->v4l2_dev, 384 v4l2_err(&fmd->v4l2_dev,
388 "Failed to register CSIS subdevice: %d\n", ret); 385 "Failed to register MIPI-CSIS.%d (%d)\n", id, ret);
389 return ret; 386 return ret;
390} 387}
391 388
392/** 389static int fimc_md_register_platform_entity(struct fimc_md *fmd,
393 * fimc_md_register_platform_entities - register FIMC and CSIS media entities 390 struct platform_device *pdev,
394 */ 391 int plat_entity)
395static int fimc_md_register_platform_entities(struct fimc_md *fmd)
396{ 392{
397 struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; 393 struct device *dev = &pdev->dev;
398 struct device_driver *driver; 394 int ret = -EPROBE_DEFER;
399 int ret, i; 395 void *drvdata;
400 396
401 driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); 397 /* Lock to ensure dev->driver won't change. */
402 if (!driver) { 398 device_lock(dev);
403 v4l2_warn(&fmd->v4l2_dev, 399
404 "%s driver not found, deffering probe\n", 400 if (!dev->driver || !try_module_get(dev->driver->owner))
405 FIMC_MODULE_NAME); 401 goto dev_unlock;
406 return -EPROBE_DEFER; 402
407 } 403 drvdata = dev_get_drvdata(dev);
408 404 /* Some subdev didn't probe succesfully id drvdata is NULL */
409 ret = driver_for_each_device(driver, NULL, fmd, 405 if (drvdata) {
410 fimc_register_callback); 406 switch (plat_entity) {
411 if (ret) 407 case IDX_FIMC:
412 return ret; 408 ret = register_fimc_entity(fmd, drvdata);
413 409 break;
414 driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); 410 case IDX_FLITE:
415 if (driver && try_module_get(driver->owner)) { 411 ret = register_fimc_lite_entity(fmd, drvdata);
416 ret = driver_for_each_device(driver, NULL, fmd,
417 fimc_lite_register_callback);
418 if (ret)
419 return ret;
420 module_put(driver->owner);
421 }
422 /*
423 * Check if there is any sensor on the MIPI-CSI2 bus and
424 * if not skip the s5p-csis module loading.
425 */
426 if (pdata == NULL)
427 return 0;
428 for (i = 0; i < pdata->num_clients; i++) {
429 if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
430 ret = 1;
431 break; 412 break;
413 case IDX_CSIS:
414 ret = register_csis_entity(fmd, pdev, drvdata);
415 break;
416 default:
417 ret = -ENODEV;
432 } 418 }
433 } 419 }
434 if (!ret)
435 return 0;
436 420
437 driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); 421 module_put(dev->driver->owner);
438 if (!driver || !try_module_get(driver->owner)) { 422dev_unlock:
439 v4l2_warn(&fmd->v4l2_dev, 423 device_unlock(dev);
440 "%s driver not found, deffering probe\n", 424 if (ret == -EPROBE_DEFER)
441 CSIS_DRIVER_NAME); 425 dev_info(&fmd->pdev->dev, "deferring %s device registration\n",
442 return -EPROBE_DEFER; 426 dev_name(dev));
427 else if (ret < 0)
428 dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n",
429 dev_name(dev), ret);
430 return ret;
431}
432
433static int fimc_md_pdev_match(struct device *dev, void *data)
434{
435 struct platform_device *pdev = to_platform_device(dev);
436 int plat_entity = -1;
437 int ret;
438 char *p;
439
440 if (!get_device(dev))
441 return -ENODEV;
442
443 if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) {
444 plat_entity = IDX_CSIS;
445 } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) {
446 plat_entity = IDX_FLITE;
447 } else {
448 p = strstr(pdev->name, "fimc");
449 if (p && *(p + 4) == 0)
450 plat_entity = IDX_FIMC;
443 } 451 }
444 452
445 return driver_for_each_device(driver, NULL, fmd, 453 if (plat_entity >= 0)
446 csis_register_callback); 454 ret = fimc_md_register_platform_entity(data, pdev,
455 plat_entity);
456 put_device(dev);
457 return 0;
447} 458}
448 459
449static void fimc_md_unregister_entities(struct fimc_md *fmd) 460static void fimc_md_unregister_entities(struct fimc_md *fmd)
@@ -477,6 +488,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
477 fimc_md_unregister_sensor(fmd->sensor[i].subdev); 488 fimc_md_unregister_sensor(fmd->sensor[i].subdev);
478 fmd->sensor[i].subdev = NULL; 489 fmd->sensor[i].subdev = NULL;
479 } 490 }
491 v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n");
480} 492}
481 493
482/** 494/**
@@ -939,7 +951,8 @@ static int fimc_md_probe(struct platform_device *pdev)
939 /* Protect the media graph while we're registering entities */ 951 /* Protect the media graph while we're registering entities */
940 mutex_lock(&fmd->media_dev.graph_mutex); 952 mutex_lock(&fmd->media_dev.graph_mutex);
941 953
942 ret = fimc_md_register_platform_entities(fmd); 954 ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
955 fimc_md_pdev_match);
943 if (ret) 956 if (ret)
944 goto err_unlock; 957 goto err_unlock;
945 958