| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * S5P/EXYNOS4 SoC series camera host interface media device driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. |
|---|
| 5 | 6 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> |
|---|
| 6 | | - * |
|---|
| 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 |
|---|
| 9 | | - * by the Free Software Foundation, either version 2 of the License, |
|---|
| 10 | | - * or (at your option) any later version. |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/bug.h> |
|---|
| .. | .. |
|---|
| 23 | 19 | #include <linux/of_platform.h> |
|---|
| 24 | 20 | #include <linux/of_device.h> |
|---|
| 25 | 21 | #include <linux/of_graph.h> |
|---|
| 22 | +#include <linux/pinctrl/consumer.h> |
|---|
| 26 | 23 | #include <linux/platform_device.h> |
|---|
| 27 | 24 | #include <linux/pm_runtime.h> |
|---|
| 28 | 25 | #include <linux/types.h> |
|---|
| .. | .. |
|---|
| 96 | 93 | switch (sd->grp_id) { |
|---|
| 97 | 94 | case GRP_ID_SENSOR: |
|---|
| 98 | 95 | sensor = sd; |
|---|
| 99 | | - /* fall through */ |
|---|
| 96 | + fallthrough; |
|---|
| 100 | 97 | case GRP_ID_FIMC_IS_SENSOR: |
|---|
| 101 | 98 | p->subdevs[IDX_SENSOR] = sd; |
|---|
| 102 | 99 | break; |
|---|
| .. | .. |
|---|
| 293 | 290 | { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, |
|---|
| 294 | 291 | }; |
|---|
| 295 | 292 | struct fimc_pipeline *p = to_fimc_pipeline(ep); |
|---|
| 296 | | - struct fimc_md *fmd = entity_to_fimc_mdev(&p->subdevs[IDX_CSIS]->entity); |
|---|
| 297 | 293 | enum fimc_subdev_index sd_id; |
|---|
| 298 | 294 | int i, ret = 0; |
|---|
| 299 | 295 | |
|---|
| 300 | 296 | if (p->subdevs[IDX_SENSOR] == NULL) { |
|---|
| 297 | + struct fimc_md *fmd; |
|---|
| 298 | + struct v4l2_subdev *sd = p->subdevs[IDX_CSIS]; |
|---|
| 299 | + |
|---|
| 300 | + if (!sd) |
|---|
| 301 | + sd = p->subdevs[IDX_FIMC]; |
|---|
| 302 | + |
|---|
| 303 | + if (!sd) { |
|---|
| 304 | + /* |
|---|
| 305 | + * If neither CSIS nor FIMC was set up, |
|---|
| 306 | + * it's impossible to have any sensors |
|---|
| 307 | + */ |
|---|
| 308 | + return -ENODEV; |
|---|
| 309 | + } |
|---|
| 310 | + |
|---|
| 311 | + fmd = entity_to_fimc_mdev(&sd->entity); |
|---|
| 312 | + |
|---|
| 301 | 313 | if (!fmd->user_subdev_api) { |
|---|
| 302 | 314 | /* |
|---|
| 303 | 315 | * Sensor must be already discovered if we |
|---|
| .. | .. |
|---|
| 383 | 395 | } |
|---|
| 384 | 396 | } |
|---|
| 385 | 397 | |
|---|
| 386 | | -/* Parse port node and register as a sub-device any sensor specified there. */ |
|---|
| 387 | | -static int fimc_md_parse_port_node(struct fimc_md *fmd, |
|---|
| 388 | | - struct device_node *port, |
|---|
| 389 | | - unsigned int index) |
|---|
| 398 | +static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, |
|---|
| 399 | + struct device_node *ep) |
|---|
| 390 | 400 | { |
|---|
| 401 | + int index = fmd->num_sensors; |
|---|
| 391 | 402 | struct fimc_source_info *pd = &fmd->sensor[index].pdata; |
|---|
| 392 | | - struct device_node *rem, *ep, *np; |
|---|
| 393 | | - struct v4l2_fwnode_endpoint endpoint; |
|---|
| 403 | + struct device_node *rem, *np; |
|---|
| 404 | + struct v4l2_async_subdev *asd; |
|---|
| 405 | + struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; |
|---|
| 394 | 406 | int ret; |
|---|
| 395 | | - |
|---|
| 396 | | - /* Assume here a port node can have only one endpoint node. */ |
|---|
| 397 | | - ep = of_get_next_child(port, NULL); |
|---|
| 398 | | - if (!ep) |
|---|
| 399 | | - return 0; |
|---|
| 400 | 407 | |
|---|
| 401 | 408 | ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint); |
|---|
| 402 | 409 | if (ret) { |
|---|
| .. | .. |
|---|
| 412 | 419 | pd->mux_id = (endpoint.base.port - 1) & 0x1; |
|---|
| 413 | 420 | |
|---|
| 414 | 421 | rem = of_graph_get_remote_port_parent(ep); |
|---|
| 415 | | - of_node_put(ep); |
|---|
| 416 | 422 | if (rem == NULL) { |
|---|
| 417 | 423 | v4l2_info(&fmd->v4l2_dev, "Remote device at %pOF not found\n", |
|---|
| 418 | 424 | ep); |
|---|
| 425 | + of_node_put(ep); |
|---|
| 419 | 426 | return 0; |
|---|
| 420 | 427 | } |
|---|
| 421 | 428 | |
|---|
| .. | .. |
|---|
| 444 | 451 | * checking parent's node name. |
|---|
| 445 | 452 | */ |
|---|
| 446 | 453 | np = of_get_parent(rem); |
|---|
| 454 | + of_node_put(rem); |
|---|
| 447 | 455 | |
|---|
| 448 | | - if (np && !of_node_cmp(np->name, "i2c-isp")) |
|---|
| 456 | + if (of_node_name_eq(np, "i2c-isp")) |
|---|
| 449 | 457 | pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; |
|---|
| 450 | 458 | else |
|---|
| 451 | 459 | pd->fimc_bus_type = pd->sensor_bus_type; |
|---|
| 460 | + of_node_put(np); |
|---|
| 452 | 461 | |
|---|
| 453 | 462 | if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { |
|---|
| 454 | | - of_node_put(rem); |
|---|
| 463 | + of_node_put(ep); |
|---|
| 455 | 464 | return -EINVAL; |
|---|
| 456 | 465 | } |
|---|
| 457 | 466 | |
|---|
| 458 | | - fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE; |
|---|
| 459 | | - fmd->sensor[index].asd.match.fwnode = of_fwnode_handle(rem); |
|---|
| 460 | | - fmd->async_subdevs[index] = &fmd->sensor[index].asd; |
|---|
| 467 | + asd = v4l2_async_notifier_add_fwnode_remote_subdev( |
|---|
| 468 | + &fmd->subdev_notifier, of_fwnode_handle(ep), sizeof(*asd)); |
|---|
| 461 | 469 | |
|---|
| 470 | + of_node_put(ep); |
|---|
| 471 | + |
|---|
| 472 | + if (IS_ERR(asd)) |
|---|
| 473 | + return PTR_ERR(asd); |
|---|
| 474 | + |
|---|
| 475 | + fmd->sensor[index].asd = asd; |
|---|
| 462 | 476 | fmd->num_sensors++; |
|---|
| 463 | 477 | |
|---|
| 464 | | - of_node_put(rem); |
|---|
| 478 | + return 0; |
|---|
| 479 | +} |
|---|
| 480 | + |
|---|
| 481 | +/* Parse port node and register as a sub-device any sensor specified there. */ |
|---|
| 482 | +static int fimc_md_parse_port_node(struct fimc_md *fmd, |
|---|
| 483 | + struct device_node *port) |
|---|
| 484 | +{ |
|---|
| 485 | + struct device_node *ep; |
|---|
| 486 | + int ret; |
|---|
| 487 | + |
|---|
| 488 | + for_each_child_of_node(port, ep) { |
|---|
| 489 | + ret = fimc_md_parse_one_endpoint(fmd, ep); |
|---|
| 490 | + if (ret < 0) |
|---|
| 491 | + return ret; |
|---|
| 492 | + } |
|---|
| 493 | + |
|---|
| 465 | 494 | return 0; |
|---|
| 466 | 495 | } |
|---|
| 467 | 496 | |
|---|
| .. | .. |
|---|
| 469 | 498 | static int fimc_md_register_sensor_entities(struct fimc_md *fmd) |
|---|
| 470 | 499 | { |
|---|
| 471 | 500 | struct device_node *parent = fmd->pdev->dev.of_node; |
|---|
| 472 | | - struct device_node *node, *ports; |
|---|
| 473 | | - int index = 0; |
|---|
| 501 | + struct device_node *ports = NULL; |
|---|
| 502 | + struct device_node *node; |
|---|
| 474 | 503 | int ret; |
|---|
| 475 | 504 | |
|---|
| 476 | 505 | /* |
|---|
| .. | .. |
|---|
| 480 | 509 | if (!fmd->pmf) |
|---|
| 481 | 510 | return -ENXIO; |
|---|
| 482 | 511 | |
|---|
| 483 | | - ret = pm_runtime_get_sync(fmd->pmf); |
|---|
| 484 | | - if (ret < 0) { |
|---|
| 485 | | - pm_runtime_put(fmd->pmf); |
|---|
| 512 | + ret = pm_runtime_resume_and_get(fmd->pmf); |
|---|
| 513 | + if (ret < 0) |
|---|
| 486 | 514 | return ret; |
|---|
| 487 | | - } |
|---|
| 488 | 515 | |
|---|
| 489 | 516 | fmd->num_sensors = 0; |
|---|
| 490 | 517 | |
|---|
| .. | .. |
|---|
| 492 | 519 | for_each_available_child_of_node(parent, node) { |
|---|
| 493 | 520 | struct device_node *port; |
|---|
| 494 | 521 | |
|---|
| 495 | | - if (of_node_cmp(node->name, "csis")) |
|---|
| 522 | + if (!of_node_name_eq(node, "csis")) |
|---|
| 496 | 523 | continue; |
|---|
| 497 | 524 | /* The csis node can have only port subnode. */ |
|---|
| 498 | 525 | port = of_get_next_child(node, NULL); |
|---|
| 499 | 526 | if (!port) |
|---|
| 500 | 527 | continue; |
|---|
| 501 | 528 | |
|---|
| 502 | | - ret = fimc_md_parse_port_node(fmd, port, index); |
|---|
| 529 | + ret = fimc_md_parse_port_node(fmd, port); |
|---|
| 503 | 530 | of_node_put(port); |
|---|
| 504 | 531 | if (ret < 0) { |
|---|
| 505 | 532 | of_node_put(node); |
|---|
| 506 | | - goto rpm_put; |
|---|
| 533 | + goto cleanup; |
|---|
| 507 | 534 | } |
|---|
| 508 | | - index++; |
|---|
| 509 | 535 | } |
|---|
| 510 | 536 | |
|---|
| 511 | 537 | /* Attach sensors listed in the parallel-ports node */ |
|---|
| .. | .. |
|---|
| 514 | 540 | goto rpm_put; |
|---|
| 515 | 541 | |
|---|
| 516 | 542 | for_each_child_of_node(ports, node) { |
|---|
| 517 | | - ret = fimc_md_parse_port_node(fmd, node, index); |
|---|
| 543 | + ret = fimc_md_parse_port_node(fmd, node); |
|---|
| 518 | 544 | if (ret < 0) { |
|---|
| 519 | 545 | of_node_put(node); |
|---|
| 520 | | - break; |
|---|
| 546 | + goto cleanup; |
|---|
| 521 | 547 | } |
|---|
| 522 | | - index++; |
|---|
| 523 | 548 | } |
|---|
| 549 | + of_node_put(ports); |
|---|
| 550 | + |
|---|
| 524 | 551 | rpm_put: |
|---|
| 552 | + pm_runtime_put(fmd->pmf); |
|---|
| 553 | + return 0; |
|---|
| 554 | + |
|---|
| 555 | +cleanup: |
|---|
| 556 | + of_node_put(ports); |
|---|
| 557 | + v4l2_async_notifier_cleanup(&fmd->subdev_notifier); |
|---|
| 525 | 558 | pm_runtime_put(fmd->pmf); |
|---|
| 526 | 559 | return ret; |
|---|
| 527 | 560 | } |
|---|
| .. | .. |
|---|
| 713 | 746 | continue; |
|---|
| 714 | 747 | |
|---|
| 715 | 748 | /* If driver of any entity isn't ready try all again later. */ |
|---|
| 716 | | - if (!strcmp(node->name, CSIS_OF_NODE_NAME)) |
|---|
| 749 | + if (of_node_name_eq(node, CSIS_OF_NODE_NAME)) |
|---|
| 717 | 750 | plat_entity = IDX_CSIS; |
|---|
| 718 | | - else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME)) |
|---|
| 751 | + else if (of_node_name_eq(node, FIMC_IS_OF_NODE_NAME)) |
|---|
| 719 | 752 | plat_entity = IDX_IS_ISP; |
|---|
| 720 | | - else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) |
|---|
| 753 | + else if (of_node_name_eq(node, FIMC_LITE_OF_NODE_NAME)) |
|---|
| 721 | 754 | plat_entity = IDX_FLITE; |
|---|
| 722 | | - else if (!strcmp(node->name, FIMC_OF_NODE_NAME) && |
|---|
| 755 | + else if (of_node_name_eq(node, FIMC_OF_NODE_NAME) && |
|---|
| 723 | 756 | !of_property_read_bool(node, "samsung,lcd-wb")) |
|---|
| 724 | 757 | plat_entity = IDX_FIMC; |
|---|
| 725 | 758 | |
|---|
| .. | .. |
|---|
| 1208 | 1241 | struct fimc_md *fmd = dev_get_drvdata(dev); |
|---|
| 1209 | 1242 | |
|---|
| 1210 | 1243 | if (fmd->user_subdev_api) |
|---|
| 1211 | | - return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); |
|---|
| 1244 | + return strscpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); |
|---|
| 1212 | 1245 | |
|---|
| 1213 | | - return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); |
|---|
| 1246 | + return strscpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); |
|---|
| 1214 | 1247 | } |
|---|
| 1215 | 1248 | |
|---|
| 1216 | 1249 | static ssize_t fimc_md_sysfs_store(struct device *dev, |
|---|
| .. | .. |
|---|
| 1245 | 1278 | static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, |
|---|
| 1246 | 1279 | fimc_md_sysfs_show, fimc_md_sysfs_store); |
|---|
| 1247 | 1280 | |
|---|
| 1248 | | -static int fimc_md_get_pinctrl(struct fimc_md *fmd) |
|---|
| 1249 | | -{ |
|---|
| 1250 | | - struct device *dev = &fmd->pdev->dev; |
|---|
| 1251 | | - struct fimc_pinctrl *pctl = &fmd->pinctl; |
|---|
| 1252 | | - |
|---|
| 1253 | | - pctl->pinctrl = devm_pinctrl_get(dev); |
|---|
| 1254 | | - if (IS_ERR(pctl->pinctrl)) |
|---|
| 1255 | | - return PTR_ERR(pctl->pinctrl); |
|---|
| 1256 | | - |
|---|
| 1257 | | - pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, |
|---|
| 1258 | | - PINCTRL_STATE_DEFAULT); |
|---|
| 1259 | | - if (IS_ERR(pctl->state_default)) |
|---|
| 1260 | | - return PTR_ERR(pctl->state_default); |
|---|
| 1261 | | - |
|---|
| 1262 | | - /* PINCTRL_STATE_IDLE is optional */ |
|---|
| 1263 | | - pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, |
|---|
| 1264 | | - PINCTRL_STATE_IDLE); |
|---|
| 1265 | | - return 0; |
|---|
| 1266 | | -} |
|---|
| 1267 | | - |
|---|
| 1268 | 1281 | static int cam_clk_prepare(struct clk_hw *hw) |
|---|
| 1269 | 1282 | { |
|---|
| 1270 | 1283 | struct cam_clk *camclk = to_cam_clk(hw); |
|---|
| 1271 | | - int ret; |
|---|
| 1272 | 1284 | |
|---|
| 1273 | 1285 | if (camclk->fmd->pmf == NULL) |
|---|
| 1274 | 1286 | return -ENODEV; |
|---|
| 1275 | 1287 | |
|---|
| 1276 | | - ret = pm_runtime_get_sync(camclk->fmd->pmf); |
|---|
| 1277 | | - return ret < 0 ? ret : 0; |
|---|
| 1288 | + return pm_runtime_resume_and_get(camclk->fmd->pmf); |
|---|
| 1278 | 1289 | } |
|---|
| 1279 | 1290 | |
|---|
| 1280 | 1291 | static void cam_clk_unprepare(struct clk_hw *hw) |
|---|
| .. | .. |
|---|
| 1367 | 1378 | |
|---|
| 1368 | 1379 | /* Find platform data for this sensor subdev */ |
|---|
| 1369 | 1380 | for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) |
|---|
| 1370 | | - if (fmd->sensor[i].asd.match.fwnode == |
|---|
| 1371 | | - of_fwnode_handle(subdev->dev->of_node)) |
|---|
| 1381 | + if (fmd->sensor[i].asd == asd) |
|---|
| 1372 | 1382 | si = &fmd->sensor[i]; |
|---|
| 1373 | 1383 | |
|---|
| 1374 | 1384 | if (si == NULL) |
|---|
| .. | .. |
|---|
| 1420 | 1430 | { |
|---|
| 1421 | 1431 | struct device *dev = &pdev->dev; |
|---|
| 1422 | 1432 | struct v4l2_device *v4l2_dev; |
|---|
| 1433 | + struct pinctrl *pinctrl; |
|---|
| 1423 | 1434 | struct fimc_md *fmd; |
|---|
| 1424 | 1435 | int ret; |
|---|
| 1425 | 1436 | |
|---|
| .. | .. |
|---|
| 1431 | 1442 | INIT_LIST_HEAD(&fmd->pipelines); |
|---|
| 1432 | 1443 | fmd->pdev = pdev; |
|---|
| 1433 | 1444 | |
|---|
| 1434 | | - strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", |
|---|
| 1445 | + strscpy(fmd->media_dev.model, "Samsung S5P FIMC", |
|---|
| 1435 | 1446 | sizeof(fmd->media_dev.model)); |
|---|
| 1436 | 1447 | fmd->media_dev.ops = &fimc_md_ops; |
|---|
| 1437 | 1448 | fmd->media_dev.dev = dev; |
|---|
| .. | .. |
|---|
| 1439 | 1450 | v4l2_dev = &fmd->v4l2_dev; |
|---|
| 1440 | 1451 | v4l2_dev->mdev = &fmd->media_dev; |
|---|
| 1441 | 1452 | v4l2_dev->notify = fimc_sensor_notify; |
|---|
| 1442 | | - strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); |
|---|
| 1453 | + strscpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); |
|---|
| 1443 | 1454 | |
|---|
| 1444 | 1455 | fmd->use_isp = fimc_md_is_isp_available(dev->of_node); |
|---|
| 1445 | 1456 | fmd->user_subdev_api = true; |
|---|
| .. | .. |
|---|
| 1449 | 1460 | ret = v4l2_device_register(dev, &fmd->v4l2_dev); |
|---|
| 1450 | 1461 | if (ret < 0) { |
|---|
| 1451 | 1462 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); |
|---|
| 1452 | | - return ret; |
|---|
| 1463 | + goto err_md; |
|---|
| 1453 | 1464 | } |
|---|
| 1454 | 1465 | |
|---|
| 1455 | 1466 | ret = fimc_md_get_clocks(fmd); |
|---|
| 1456 | 1467 | if (ret) |
|---|
| 1457 | | - goto err_md; |
|---|
| 1468 | + goto err_v4l2dev; |
|---|
| 1458 | 1469 | |
|---|
| 1459 | | - ret = fimc_md_get_pinctrl(fmd); |
|---|
| 1460 | | - if (ret < 0) { |
|---|
| 1461 | | - if (ret != EPROBE_DEFER) |
|---|
| 1470 | + pinctrl = devm_pinctrl_get(dev); |
|---|
| 1471 | + if (IS_ERR(pinctrl)) { |
|---|
| 1472 | + ret = PTR_ERR(pinctrl); |
|---|
| 1473 | + if (ret != -EPROBE_DEFER) |
|---|
| 1462 | 1474 | dev_err(dev, "Failed to get pinctrl: %d\n", ret); |
|---|
| 1463 | 1475 | goto err_clk; |
|---|
| 1464 | 1476 | } |
|---|
| 1465 | 1477 | |
|---|
| 1466 | 1478 | platform_set_drvdata(pdev, fmd); |
|---|
| 1479 | + |
|---|
| 1480 | + v4l2_async_notifier_init(&fmd->subdev_notifier); |
|---|
| 1467 | 1481 | |
|---|
| 1468 | 1482 | ret = fimc_md_register_platform_entities(fmd, dev->of_node); |
|---|
| 1469 | 1483 | if (ret) |
|---|
| .. | .. |
|---|
| 1475 | 1489 | |
|---|
| 1476 | 1490 | ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
|---|
| 1477 | 1491 | if (ret) |
|---|
| 1478 | | - goto err_m_ent; |
|---|
| 1492 | + goto err_cleanup; |
|---|
| 1479 | 1493 | /* |
|---|
| 1480 | 1494 | * FIMC platform devices need to be registered before the sclk_cam |
|---|
| 1481 | 1495 | * clocks provider, as one of these devices needs to be activated |
|---|
| .. | .. |
|---|
| 1488 | 1502 | } |
|---|
| 1489 | 1503 | |
|---|
| 1490 | 1504 | if (fmd->num_sensors > 0) { |
|---|
| 1491 | | - fmd->subdev_notifier.subdevs = fmd->async_subdevs; |
|---|
| 1492 | | - fmd->subdev_notifier.num_subdevs = fmd->num_sensors; |
|---|
| 1493 | 1505 | fmd->subdev_notifier.ops = &subdev_notifier_ops; |
|---|
| 1494 | 1506 | fmd->num_sensors = 0; |
|---|
| 1495 | 1507 | |
|---|
| .. | .. |
|---|
| 1505 | 1517 | fimc_md_unregister_clk_provider(fmd); |
|---|
| 1506 | 1518 | err_attr: |
|---|
| 1507 | 1519 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
|---|
| 1508 | | -err_clk: |
|---|
| 1509 | | - fimc_md_put_clocks(fmd); |
|---|
| 1520 | +err_cleanup: |
|---|
| 1521 | + v4l2_async_notifier_cleanup(&fmd->subdev_notifier); |
|---|
| 1510 | 1522 | err_m_ent: |
|---|
| 1511 | 1523 | fimc_md_unregister_entities(fmd); |
|---|
| 1524 | +err_clk: |
|---|
| 1525 | + fimc_md_put_clocks(fmd); |
|---|
| 1526 | +err_v4l2dev: |
|---|
| 1527 | + v4l2_device_unregister(&fmd->v4l2_dev); |
|---|
| 1512 | 1528 | err_md: |
|---|
| 1513 | 1529 | media_device_cleanup(&fmd->media_dev); |
|---|
| 1514 | | - v4l2_device_unregister(&fmd->v4l2_dev); |
|---|
| 1515 | 1530 | return ret; |
|---|
| 1516 | 1531 | } |
|---|
| 1517 | 1532 | |
|---|
| .. | .. |
|---|
| 1524 | 1539 | |
|---|
| 1525 | 1540 | fimc_md_unregister_clk_provider(fmd); |
|---|
| 1526 | 1541 | v4l2_async_notifier_unregister(&fmd->subdev_notifier); |
|---|
| 1542 | + v4l2_async_notifier_cleanup(&fmd->subdev_notifier); |
|---|
| 1527 | 1543 | |
|---|
| 1528 | 1544 | v4l2_device_unregister(&fmd->v4l2_dev); |
|---|
| 1529 | 1545 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
|---|
| .. | .. |
|---|
| 1566 | 1582 | if (ret) |
|---|
| 1567 | 1583 | return ret; |
|---|
| 1568 | 1584 | |
|---|
| 1569 | | - return platform_driver_register(&fimc_md_driver); |
|---|
| 1585 | + ret = platform_driver_register(&fimc_md_driver); |
|---|
| 1586 | + if (ret) |
|---|
| 1587 | + fimc_unregister_driver(); |
|---|
| 1588 | + |
|---|
| 1589 | + return ret; |
|---|
| 1570 | 1590 | } |
|---|
| 1571 | 1591 | |
|---|
| 1572 | 1592 | static void __exit fimc_md_exit(void) |
|---|