[media] omap3isp: Add support for the Device Tree
authorSakari Ailus <sakari.ailus@iki.fi>
Wed, 25 Mar 2015 22:57:38 +0000 (19:57 -0300)
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>
Thu, 2 Apr 2015 19:47:48 +0000 (16:47 -0300)
Add the ISP device to omap3 DT include file and add support to the driver to
use it.

Also obtain information on the external entities and the ISP configuration
related to them through the Device Tree in addition to the platform data.

Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/omap3isp/isp.h
drivers/media/platform/omap3isp/ispcsiphy.c

index 992e74c3471dedf89a6fc5d4dab1fd10fc0ef714..9ef7e035aa6ce4d999f18abfe2d503ed6911bd28 100644 (file)
@@ -64,6 +64,7 @@
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
 
 #include "isp.h"
 #include "ispreg.h"
@@ -1991,6 +1992,13 @@ static int isp_register_entities(struct isp_device *isp)
        if (ret < 0)
                goto done;
 
+       /*
+        * Device Tree --- the external sub-devices will be registered
+        * later. The same goes for the sub-device node registration.
+        */
+       if (isp->dev->of_node)
+               return 0;
+
        /* Register external entities */
        for (isp_subdev = pdata ? pdata->subdevs : NULL;
             isp_subdev && isp_subdev->board_info; isp_subdev++) {
@@ -2016,8 +2024,10 @@ static int isp_register_entities(struct isp_device *isp)
        ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
 
 done:
-       if (ret < 0)
+       if (ret < 0) {
                isp_unregister_entities(isp);
+               v4l2_async_notifier_unregister(&isp->notifier);
+       }
 
        return ret;
 }
@@ -2232,6 +2242,7 @@ static int isp_remove(struct platform_device *pdev)
 {
        struct isp_device *isp = platform_get_drvdata(pdev);
 
+       v4l2_async_notifier_unregister(&isp->notifier);
        isp_unregister_entities(isp);
        isp_cleanup_modules(isp);
        isp_xclk_cleanup(isp);
@@ -2243,6 +2254,156 @@ static int isp_remove(struct platform_device *pdev)
        return 0;
 }
 
+enum isp_of_phy {
+       ISP_OF_PHY_PARALLEL = 0,
+       ISP_OF_PHY_CSIPHY1,
+       ISP_OF_PHY_CSIPHY2,
+};
+
+static int isp_of_parse_node(struct device *dev, struct device_node *node,
+                            struct isp_async_subdev *isd)
+{
+       struct isp_bus_cfg *buscfg = &isd->bus;
+       struct v4l2_of_endpoint vep;
+       unsigned int i;
+
+       v4l2_of_parse_endpoint(node, &vep);
+
+       dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name,
+               vep.base.port);
+
+       switch (vep.base.port) {
+       case ISP_OF_PHY_PARALLEL:
+               buscfg->interface = ISP_INTERFACE_PARALLEL;
+               buscfg->bus.parallel.data_lane_shift =
+                       vep.bus.parallel.data_shift;
+               buscfg->bus.parallel.clk_pol =
+                       !!(vep.bus.parallel.flags
+                          & V4L2_MBUS_PCLK_SAMPLE_FALLING);
+               buscfg->bus.parallel.hs_pol =
+                       !!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW);
+               buscfg->bus.parallel.vs_pol =
+                       !!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW);
+               buscfg->bus.parallel.fld_pol =
+                       !!(vep.bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW);
+               buscfg->bus.parallel.data_pol =
+                       !!(vep.bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW);
+               break;
+
+       case ISP_OF_PHY_CSIPHY1:
+       case ISP_OF_PHY_CSIPHY2:
+               /* FIXME: always assume CSI-2 for now. */
+               switch (vep.base.port) {
+               case ISP_OF_PHY_CSIPHY1:
+                       buscfg->interface = ISP_INTERFACE_CSI2C_PHY1;
+                       break;
+               case ISP_OF_PHY_CSIPHY2:
+                       buscfg->interface = ISP_INTERFACE_CSI2A_PHY2;
+                       break;
+               }
+               buscfg->bus.csi2.lanecfg.clk.pos = vep.bus.mipi_csi2.clock_lane;
+               buscfg->bus.csi2.lanecfg.clk.pol =
+                       vep.bus.mipi_csi2.lane_polarities[0];
+               dev_dbg(dev, "clock lane polarity %u, pos %u\n",
+                       buscfg->bus.csi2.lanecfg.clk.pol,
+                       buscfg->bus.csi2.lanecfg.clk.pos);
+
+               for (i = 0; i < ISP_CSIPHY2_NUM_DATA_LANES; i++) {
+                       buscfg->bus.csi2.lanecfg.data[i].pos =
+                               vep.bus.mipi_csi2.data_lanes[i];
+                       buscfg->bus.csi2.lanecfg.data[i].pol =
+                               vep.bus.mipi_csi2.lane_polarities[i + 1];
+                       dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i,
+                               buscfg->bus.csi2.lanecfg.data[i].pol,
+                               buscfg->bus.csi2.lanecfg.data[i].pos);
+               }
+
+               /*
+                * FIXME: now we assume the CRC is always there.
+                * Implement a way to obtain this information from the
+                * sensor. Frame descriptors, perhaps?
+                */
+               buscfg->bus.csi2.crc = 1;
+               break;
+
+       default:
+               dev_warn(dev, "%s: invalid interface %u\n", node->full_name,
+                        vep.base.port);
+               break;
+       }
+
+       return 0;
+}
+
+static int isp_of_parse_nodes(struct device *dev,
+                             struct v4l2_async_notifier *notifier)
+{
+       struct device_node *node = NULL;
+
+       notifier->subdevs = devm_kcalloc(
+               dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL);
+       if (!notifier->subdevs)
+               return -ENOMEM;
+
+       while (notifier->num_subdevs < ISP_MAX_SUBDEVS &&
+              (node = of_graph_get_next_endpoint(dev->of_node, node))) {
+               struct isp_async_subdev *isd;
+
+               isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL);
+               if (!isd) {
+                       of_node_put(node);
+                       return -ENOMEM;
+               }
+
+               notifier->subdevs[notifier->num_subdevs] = &isd->asd;
+
+               if (isp_of_parse_node(dev, node, isd)) {
+                       of_node_put(node);
+                       return -EINVAL;
+               }
+
+               isd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+               of_node_put(node);
+               if (!isd->asd.match.of.node) {
+                       dev_warn(dev, "bad remote port parent\n");
+                       return -EINVAL;
+               }
+
+               isd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+               notifier->num_subdevs++;
+       }
+
+       return notifier->num_subdevs;
+}
+
+static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async,
+                                    struct v4l2_subdev *subdev,
+                                    struct v4l2_async_subdev *asd)
+{
+       struct isp_device *isp = container_of(async, struct isp_device,
+                                             notifier);
+       struct isp_async_subdev *isd =
+               container_of(asd, struct isp_async_subdev, asd);
+       int ret;
+
+       ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface);
+       if (ret < 0)
+               return ret;
+
+       isd->sd = subdev;
+       isd->sd->host_priv = &isd->bus;
+
+       return ret;
+}
+
+static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
+{
+       struct isp_device *isp = container_of(async, struct isp_device,
+                                             notifier);
+
+       return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+}
+
 /*
  * isp_probe - Probe ISP platform device
  * @pdev: Pointer to ISP platform device
@@ -2256,7 +2417,6 @@ static int isp_remove(struct platform_device *pdev)
  */
 static int isp_probe(struct platform_device *pdev)
 {
-       struct isp_platform_data *pdata = pdev->dev.platform_data;
        struct isp_device *isp;
        struct resource *mem;
        int ret;
@@ -2268,13 +2428,42 @@ static int isp_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+               ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type",
+                                          &isp->phy_type);
+               if (ret)
+                       return ret;
+
+               isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+                                                             "syscon");
+               if (IS_ERR(isp->syscon))
+                       return PTR_ERR(isp->syscon);
+
+               ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1,
+                                                &isp->syscon_offset);
+               if (ret)
+                       return ret;
+
+               ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier);
+               if (ret < 0)
+                       return ret;
+               ret = v4l2_async_notifier_register(&isp->v4l2_dev,
+                                                  &isp->notifier);
+               if (ret)
+                       return ret;
+       } else {
+               isp->pdata = pdev->dev.platform_data;
+               isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0");
+               if (IS_ERR(isp->syscon))
+                       return PTR_ERR(isp->syscon);
+       }
+
        isp->autoidle = autoidle;
 
        mutex_init(&isp->isp_mutex);
        spin_lock_init(&isp->stat_lock);
 
        isp->dev = &pdev->dev;
-       isp->pdata = pdata;
        isp->ref_count = 0;
 
        ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32));
@@ -2346,6 +2535,11 @@ static int isp_probe(struct platform_device *pdev)
                goto error_isp;
        }
 
+       if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) {
+               isp->syscon_offset = isp_res_maps[m].syscon_offset;
+               isp->phy_type = isp_res_maps[m].phy_type;
+       }
+
        for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++)
                isp->mmio_base[i] =
                        isp->mmio_base[0] + isp_res_maps[m].offset[i];
@@ -2358,15 +2552,6 @@ static int isp_probe(struct platform_device *pdev)
        isp->mmio_hist_base_phys =
                mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST];
 
-       isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0");
-       if (IS_ERR(isp->syscon)) {
-               ret = PTR_ERR(isp->syscon);
-               goto error_isp;
-       }
-
-       isp->syscon_offset = isp_res_maps[m].syscon_offset;
-       isp->phy_type = isp_res_maps[m].phy_type;
-
        /* IOMMU */
        ret = isp_attach_iommu(isp);
        if (ret < 0) {
@@ -2394,6 +2579,9 @@ static int isp_probe(struct platform_device *pdev)
        if (ret < 0)
                goto error_iommu;
 
+       isp->notifier.bound = isp_subdev_notifier_bound;
+       isp->notifier.complete = isp_subdev_notifier_complete;
+
        ret = isp_register_entities(isp);
        if (ret < 0)
                goto error_modules;
@@ -2429,6 +2617,11 @@ static struct platform_device_id omap3isp_id_table[] = {
 };
 MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
 
+static const struct of_device_id omap3isp_of_table[] = {
+       { .compatible = "ti,omap3-isp" },
+       { },
+};
+
 static struct platform_driver omap3isp_driver = {
        .probe = isp_probe,
        .remove = isp_remove,
@@ -2436,6 +2629,7 @@ static struct platform_driver omap3isp_driver = {
        .driver = {
                .name = "omap3isp",
                .pm     = &omap3isp_pm_ops,
+               .of_match_table = omap3isp_of_table,
        },
 };
 
index dcb7d20a2e53e30571e7e5ebb573bcecfcd64051..431224ec09edaa347cdb2cd0044787962e7dfc91 100644 (file)
@@ -18,6 +18,7 @@
 #define OMAP3_ISP_CORE_H
 
 #include <media/omap3isp.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
 #include <linux/clk-provider.h>
 #include <linux/device.h>
@@ -178,6 +179,7 @@ struct isp_xclk {
  */
 struct isp_device {
        struct v4l2_device v4l2_dev;
+       struct v4l2_async_notifier notifier;
        struct media_device media_dev;
        struct device *dev;
        u32 revision;
@@ -224,6 +226,15 @@ struct isp_device {
 
        unsigned int sbl_resources;
        unsigned int subclk_resources;
+
+#define ISP_MAX_SUBDEVS                8
+       struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS];
+};
+
+struct isp_async_subdev {
+       struct v4l2_subdev *sd;
+       struct isp_bus_cfg bus;
+       struct v4l2_async_subdev asd;
 };
 
 #define v4l2_dev_to_isp_device(dev) \
index d91dde19eb142d9a1a46732d8e67494c682d265f..495447d66cfddb0a5b7785c1a6f5ba12c0fe3a42 100644 (file)
@@ -173,6 +173,13 @@ static int omap3isp_csiphy_config(struct isp_csiphy *phy)
        unsigned int i;
        u32 reg;
 
+       if (!buscfg) {
+               struct isp_async_subdev *isd =
+                       container_of(pipe->external->asd,
+                                    struct isp_async_subdev, asd);
+               buscfg = &isd->bus;
+       }
+
        if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1
            || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2)
                lanes = &buscfg->bus.ccp2.lanecfg;