diff options
author | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2012-12-12 06:16:05 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-02-05 14:49:56 -0500 |
commit | 7b43a6f3f517109c2912d82f7f666a84420689bc (patch) | |
tree | d7313ea9607b53640209683ed15aaa1bfcd7f8db /drivers/media/platform | |
parent | 969e877cc1e6e91dc76f973d08ad70e2065c56ae (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>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r-- | drivers/media/platform/s5p-fimc/fimc-mdevice.c | 203 |
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 | */ |
317 | static int fimc_register_callback(struct device *dev, void *p) | 317 | |
318 | static 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 | ||
342 | static int fimc_lite_register_callback(struct device *dev, void *p) | 341 | static 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 | ||
366 | static int csis_register_callback(struct device *dev, void *p) | 364 | static 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 | /** | 389 | static 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) |
395 | static 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)) { | 422 | dev_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 | |||
433 | static 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 | ||
449 | static void fimc_md_unregister_entities(struct fimc_md *fmd) | 460 | static 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 | ||