|
36 | 36 | #include <media/v4l2-common.h>
|
37 | 37 | #include <media/v4l2-ioctl.h>
|
38 | 38 | #include <media/v4l2-dev.h>
|
| 39 | +#include <media/v4l2-of.h> |
39 | 40 | #include <media/videobuf-core.h>
|
40 | 41 | #include <media/videobuf2-core.h>
|
41 | 42 |
|
@@ -1585,6 +1586,130 @@ static void scan_async_host(struct soc_camera_host *ici)
|
1585 | 1586 | #define scan_async_host(ici) do {} while (0)
|
1586 | 1587 | #endif
|
1587 | 1588 |
|
| 1589 | +#ifdef CONFIG_OF |
| 1590 | + |
| 1591 | +struct soc_of_info { |
| 1592 | + struct soc_camera_async_subdev sasd; |
| 1593 | + struct soc_camera_async_client sasc; |
| 1594 | + struct v4l2_async_subdev *subdev; |
| 1595 | +}; |
| 1596 | + |
| 1597 | +static int soc_of_bind(struct soc_camera_host *ici, |
| 1598 | + struct device_node *ep, |
| 1599 | + struct device_node *remote) |
| 1600 | +{ |
| 1601 | + struct soc_camera_device *icd; |
| 1602 | + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; |
| 1603 | + struct soc_camera_async_client *sasc; |
| 1604 | + struct soc_of_info *info; |
| 1605 | + struct i2c_client *client; |
| 1606 | + char clk_name[V4L2_SUBDEV_NAME_SIZE]; |
| 1607 | + int ret; |
| 1608 | + |
| 1609 | + /* allocate a new subdev and add match info to it */ |
| 1610 | + info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), |
| 1611 | + GFP_KERNEL); |
| 1612 | + if (!info) |
| 1613 | + return -ENOMEM; |
| 1614 | + |
| 1615 | + info->sasd.asd.match.of.node = remote; |
| 1616 | + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF; |
| 1617 | + info->subdev = &info->sasd.asd; |
| 1618 | + |
| 1619 | + /* Or shall this be managed by the soc-camera device? */ |
| 1620 | + sasc = &info->sasc; |
| 1621 | + |
| 1622 | + /* HACK: just need a != NULL */ |
| 1623 | + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); |
| 1624 | + |
| 1625 | + ret = soc_camera_dyn_pdev(&sdesc, sasc); |
| 1626 | + if (ret < 0) |
| 1627 | + goto eallocpdev; |
| 1628 | + |
| 1629 | + sasc->sensor = &info->sasd.asd; |
| 1630 | + |
| 1631 | + icd = soc_camera_add_pdev(sasc); |
| 1632 | + if (!icd) { |
| 1633 | + ret = -ENOMEM; |
| 1634 | + goto eaddpdev; |
| 1635 | + } |
| 1636 | + |
| 1637 | + sasc->notifier.subdevs = &info->subdev; |
| 1638 | + sasc->notifier.num_subdevs = 1; |
| 1639 | + sasc->notifier.bound = soc_camera_async_bound; |
| 1640 | + sasc->notifier.unbind = soc_camera_async_unbind; |
| 1641 | + sasc->notifier.complete = soc_camera_async_complete; |
| 1642 | + |
| 1643 | + icd->sasc = sasc; |
| 1644 | + icd->parent = ici->v4l2_dev.dev; |
| 1645 | + |
| 1646 | + client = of_find_i2c_device_by_node(remote); |
| 1647 | + |
| 1648 | + if (client) |
| 1649 | + snprintf(clk_name, sizeof(clk_name), "%d-%04x", |
| 1650 | + client->adapter->nr, client->addr); |
| 1651 | + else |
| 1652 | + snprintf(clk_name, sizeof(clk_name), "of-%s", |
| 1653 | + of_node_full_name(remote)); |
| 1654 | + |
| 1655 | + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); |
| 1656 | + if (IS_ERR(icd->clk)) { |
| 1657 | + ret = PTR_ERR(icd->clk); |
| 1658 | + goto eclkreg; |
| 1659 | + } |
| 1660 | + |
| 1661 | + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); |
| 1662 | + if (!ret) |
| 1663 | + return 0; |
| 1664 | +eclkreg: |
| 1665 | + icd->clk = NULL; |
| 1666 | + platform_device_del(sasc->pdev); |
| 1667 | +eaddpdev: |
| 1668 | + platform_device_put(sasc->pdev); |
| 1669 | +eallocpdev: |
| 1670 | + devm_kfree(ici->v4l2_dev.dev, sasc); |
| 1671 | + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); |
| 1672 | + |
| 1673 | + return ret; |
| 1674 | +} |
| 1675 | + |
| 1676 | +static void scan_of_host(struct soc_camera_host *ici) |
| 1677 | +{ |
| 1678 | + struct device *dev = ici->v4l2_dev.dev; |
| 1679 | + struct device_node *np = dev->of_node; |
| 1680 | + struct device_node *epn = NULL, *ren; |
| 1681 | + unsigned int i; |
| 1682 | + |
| 1683 | + for (i = 0; ; i++) { |
| 1684 | + epn = of_graph_get_next_endpoint(np, epn); |
| 1685 | + if (!epn) |
| 1686 | + break; |
| 1687 | + |
| 1688 | + ren = of_graph_get_remote_port(epn); |
| 1689 | + if (!ren) { |
| 1690 | + dev_notice(dev, "no remote for %s\n", |
| 1691 | + of_node_full_name(epn)); |
| 1692 | + continue; |
| 1693 | + } |
| 1694 | + |
| 1695 | + /* so we now have a remote node to connect */ |
| 1696 | + if (!i) |
| 1697 | + soc_of_bind(ici, epn, ren->parent); |
| 1698 | + |
| 1699 | + of_node_put(epn); |
| 1700 | + of_node_put(ren); |
| 1701 | + |
| 1702 | + if (i) { |
| 1703 | + dev_err(dev, "multiple subdevices aren't supported yet!\n"); |
| 1704 | + break; |
| 1705 | + } |
| 1706 | + } |
| 1707 | +} |
| 1708 | + |
| 1709 | +#else |
| 1710 | +static inline void scan_of_host(struct soc_camera_host *ici) { } |
| 1711 | +#endif |
| 1712 | + |
1588 | 1713 | /* Called during host-driver probe */
|
1589 | 1714 | static int soc_camera_probe(struct soc_camera_host *ici,
|
1590 | 1715 | struct soc_camera_device *icd)
|
@@ -1836,7 +1961,9 @@ int soc_camera_host_register(struct soc_camera_host *ici)
|
1836 | 1961 | mutex_init(&ici->host_lock);
|
1837 | 1962 | mutex_init(&ici->clk_lock);
|
1838 | 1963 |
|
1839 |
| - if (ici->asd_sizes) |
| 1964 | + if (ici->v4l2_dev.dev->of_node) |
| 1965 | + scan_of_host(ici); |
| 1966 | + else if (ici->asd_sizes) |
1840 | 1967 | /*
|
1841 | 1968 | * No OF, host with a list of subdevices. Don't try to mix
|
1842 | 1969 | * modes by initialising some groups statically and some
|
|
0 commit comments