.. | .. |
---|
| 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_fwnode_endpoint endpoint = { .bus_type = 0 }; |
---|
394 | 405 | 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 | 406 | |
---|
401 | 407 | ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint); |
---|
402 | 408 | if (ret) { |
---|
.. | .. |
---|
445 | 451 | */ |
---|
446 | 452 | np = of_get_parent(rem); |
---|
447 | 453 | |
---|
448 | | - if (np && !of_node_cmp(np->name, "i2c-isp")) |
---|
| 454 | + if (of_node_name_eq(np, "i2c-isp")) |
---|
449 | 455 | pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; |
---|
450 | 456 | else |
---|
451 | 457 | pd->fimc_bus_type = pd->sensor_bus_type; |
---|
| 458 | + of_node_put(np); |
---|
452 | 459 | |
---|
453 | 460 | if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { |
---|
454 | 461 | of_node_put(rem); |
---|
.. | .. |
---|
457 | 464 | |
---|
458 | 465 | fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE; |
---|
459 | 466 | fmd->sensor[index].asd.match.fwnode = of_fwnode_handle(rem); |
---|
460 | | - fmd->async_subdevs[index] = &fmd->sensor[index].asd; |
---|
| 467 | + |
---|
| 468 | + ret = v4l2_async_notifier_add_subdev(&fmd->subdev_notifier, |
---|
| 469 | + &fmd->sensor[index].asd); |
---|
| 470 | + if (ret) { |
---|
| 471 | + of_node_put(rem); |
---|
| 472 | + return ret; |
---|
| 473 | + } |
---|
461 | 474 | |
---|
462 | 475 | fmd->num_sensors++; |
---|
463 | 476 | |
---|
464 | | - of_node_put(rem); |
---|
| 477 | + return 0; |
---|
| 478 | +} |
---|
| 479 | + |
---|
| 480 | +/* Parse port node and register as a sub-device any sensor specified there. */ |
---|
| 481 | +static int fimc_md_parse_port_node(struct fimc_md *fmd, |
---|
| 482 | + struct device_node *port) |
---|
| 483 | +{ |
---|
| 484 | + struct device_node *ep; |
---|
| 485 | + int ret; |
---|
| 486 | + |
---|
| 487 | + for_each_child_of_node(port, ep) { |
---|
| 488 | + ret = fimc_md_parse_one_endpoint(fmd, ep); |
---|
| 489 | + if (ret < 0) |
---|
| 490 | + return ret; |
---|
| 491 | + } |
---|
| 492 | + |
---|
465 | 493 | return 0; |
---|
466 | 494 | } |
---|
467 | 495 | |
---|
.. | .. |
---|
469 | 497 | static int fimc_md_register_sensor_entities(struct fimc_md *fmd) |
---|
470 | 498 | { |
---|
471 | 499 | struct device_node *parent = fmd->pdev->dev.of_node; |
---|
472 | | - struct device_node *node, *ports; |
---|
473 | | - int index = 0; |
---|
| 500 | + struct device_node *ports = NULL; |
---|
| 501 | + struct device_node *node; |
---|
474 | 502 | int ret; |
---|
475 | 503 | |
---|
476 | 504 | /* |
---|
.. | .. |
---|
480 | 508 | if (!fmd->pmf) |
---|
481 | 509 | return -ENXIO; |
---|
482 | 510 | |
---|
483 | | - ret = pm_runtime_get_sync(fmd->pmf); |
---|
484 | | - if (ret < 0) { |
---|
485 | | - pm_runtime_put(fmd->pmf); |
---|
| 511 | + ret = pm_runtime_resume_and_get(fmd->pmf); |
---|
| 512 | + if (ret < 0) |
---|
486 | 513 | return ret; |
---|
487 | | - } |
---|
488 | 514 | |
---|
489 | 515 | fmd->num_sensors = 0; |
---|
490 | 516 | |
---|
.. | .. |
---|
492 | 518 | for_each_available_child_of_node(parent, node) { |
---|
493 | 519 | struct device_node *port; |
---|
494 | 520 | |
---|
495 | | - if (of_node_cmp(node->name, "csis")) |
---|
| 521 | + if (!of_node_name_eq(node, "csis")) |
---|
496 | 522 | continue; |
---|
497 | 523 | /* The csis node can have only port subnode. */ |
---|
498 | 524 | port = of_get_next_child(node, NULL); |
---|
499 | 525 | if (!port) |
---|
500 | 526 | continue; |
---|
501 | 527 | |
---|
502 | | - ret = fimc_md_parse_port_node(fmd, port, index); |
---|
| 528 | + ret = fimc_md_parse_port_node(fmd, port); |
---|
503 | 529 | of_node_put(port); |
---|
504 | 530 | if (ret < 0) { |
---|
505 | 531 | of_node_put(node); |
---|
506 | | - goto rpm_put; |
---|
| 532 | + goto cleanup; |
---|
507 | 533 | } |
---|
508 | | - index++; |
---|
509 | 534 | } |
---|
510 | 535 | |
---|
511 | 536 | /* Attach sensors listed in the parallel-ports node */ |
---|
.. | .. |
---|
514 | 539 | goto rpm_put; |
---|
515 | 540 | |
---|
516 | 541 | for_each_child_of_node(ports, node) { |
---|
517 | | - ret = fimc_md_parse_port_node(fmd, node, index); |
---|
| 542 | + ret = fimc_md_parse_port_node(fmd, node); |
---|
518 | 543 | if (ret < 0) { |
---|
519 | 544 | of_node_put(node); |
---|
520 | | - break; |
---|
| 545 | + goto cleanup; |
---|
521 | 546 | } |
---|
522 | | - index++; |
---|
523 | 547 | } |
---|
| 548 | + of_node_put(ports); |
---|
| 549 | + |
---|
524 | 550 | rpm_put: |
---|
| 551 | + pm_runtime_put(fmd->pmf); |
---|
| 552 | + return 0; |
---|
| 553 | + |
---|
| 554 | +cleanup: |
---|
| 555 | + of_node_put(ports); |
---|
| 556 | + v4l2_async_notifier_cleanup(&fmd->subdev_notifier); |
---|
525 | 557 | pm_runtime_put(fmd->pmf); |
---|
526 | 558 | return ret; |
---|
527 | 559 | } |
---|
.. | .. |
---|
713 | 745 | continue; |
---|
714 | 746 | |
---|
715 | 747 | /* If driver of any entity isn't ready try all again later. */ |
---|
716 | | - if (!strcmp(node->name, CSIS_OF_NODE_NAME)) |
---|
| 748 | + if (of_node_name_eq(node, CSIS_OF_NODE_NAME)) |
---|
717 | 749 | plat_entity = IDX_CSIS; |
---|
718 | | - else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME)) |
---|
| 750 | + else if (of_node_name_eq(node, FIMC_IS_OF_NODE_NAME)) |
---|
719 | 751 | plat_entity = IDX_IS_ISP; |
---|
720 | | - else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) |
---|
| 752 | + else if (of_node_name_eq(node, FIMC_LITE_OF_NODE_NAME)) |
---|
721 | 753 | plat_entity = IDX_FLITE; |
---|
722 | | - else if (!strcmp(node->name, FIMC_OF_NODE_NAME) && |
---|
| 754 | + else if (of_node_name_eq(node, FIMC_OF_NODE_NAME) && |
---|
723 | 755 | !of_property_read_bool(node, "samsung,lcd-wb")) |
---|
724 | 756 | plat_entity = IDX_FIMC; |
---|
725 | 757 | |
---|
.. | .. |
---|
1208 | 1240 | struct fimc_md *fmd = dev_get_drvdata(dev); |
---|
1209 | 1241 | |
---|
1210 | 1242 | if (fmd->user_subdev_api) |
---|
1211 | | - return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); |
---|
| 1243 | + return strscpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); |
---|
1212 | 1244 | |
---|
1213 | | - return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); |
---|
| 1245 | + return strscpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); |
---|
1214 | 1246 | } |
---|
1215 | 1247 | |
---|
1216 | 1248 | static ssize_t fimc_md_sysfs_store(struct device *dev, |
---|
.. | .. |
---|
1245 | 1277 | static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, |
---|
1246 | 1278 | fimc_md_sysfs_show, fimc_md_sysfs_store); |
---|
1247 | 1279 | |
---|
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 | 1280 | static int cam_clk_prepare(struct clk_hw *hw) |
---|
1269 | 1281 | { |
---|
1270 | 1282 | struct cam_clk *camclk = to_cam_clk(hw); |
---|
1271 | | - int ret; |
---|
1272 | 1283 | |
---|
1273 | 1284 | if (camclk->fmd->pmf == NULL) |
---|
1274 | 1285 | return -ENODEV; |
---|
1275 | 1286 | |
---|
1276 | | - ret = pm_runtime_get_sync(camclk->fmd->pmf); |
---|
1277 | | - return ret < 0 ? ret : 0; |
---|
| 1287 | + return pm_runtime_resume_and_get(camclk->fmd->pmf); |
---|
1278 | 1288 | } |
---|
1279 | 1289 | |
---|
1280 | 1290 | static void cam_clk_unprepare(struct clk_hw *hw) |
---|
.. | .. |
---|
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) { |
---|
| 1470 | + pinctrl = devm_pinctrl_get(dev); |
---|
| 1471 | + if (IS_ERR(pinctrl)) { |
---|
| 1472 | + ret = PTR_ERR(pinctrl); |
---|
1461 | 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); |
---|