|
@@ -64,6 +64,7 @@
|
|
|
|
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-device.h>
|
|
|
|
+#include <media/v4l2-of.h>
|
|
|
|
|
|
#include "isp.h"
|
|
#include "isp.h"
|
|
#include "ispreg.h"
|
|
#include "ispreg.h"
|
|
@@ -1991,6 +1992,13 @@ static int isp_register_entities(struct isp_device *isp)
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto done;
|
|
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 */
|
|
/* Register external entities */
|
|
for (isp_subdev = pdata ? pdata->subdevs : NULL;
|
|
for (isp_subdev = pdata ? pdata->subdevs : NULL;
|
|
isp_subdev && isp_subdev->board_info; isp_subdev++) {
|
|
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);
|
|
ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
|
|
|
|
|
|
done:
|
|
done:
|
|
- if (ret < 0)
|
|
|
|
|
|
+ if (ret < 0) {
|
|
isp_unregister_entities(isp);
|
|
isp_unregister_entities(isp);
|
|
|
|
+ v4l2_async_notifier_unregister(&isp->notifier);
|
|
|
|
+ }
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -2232,6 +2242,7 @@ static int isp_remove(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct isp_device *isp = platform_get_drvdata(pdev);
|
|
struct isp_device *isp = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
+ v4l2_async_notifier_unregister(&isp->notifier);
|
|
isp_unregister_entities(isp);
|
|
isp_unregister_entities(isp);
|
|
isp_cleanup_modules(isp);
|
|
isp_cleanup_modules(isp);
|
|
isp_xclk_cleanup(isp);
|
|
isp_xclk_cleanup(isp);
|
|
@@ -2243,6 +2254,156 @@ static int isp_remove(struct platform_device *pdev)
|
|
return 0;
|
|
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
|
|
* isp_probe - Probe ISP platform device
|
|
* @pdev: Pointer to 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)
|
|
static int isp_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
- struct isp_platform_data *pdata = pdev->dev.platform_data;
|
|
|
|
struct isp_device *isp;
|
|
struct isp_device *isp;
|
|
struct resource *mem;
|
|
struct resource *mem;
|
|
int ret;
|
|
int ret;
|
|
@@ -2268,13 +2428,42 @@ static int isp_probe(struct platform_device *pdev)
|
|
return -ENOMEM;
|
|
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;
|
|
isp->autoidle = autoidle;
|
|
|
|
|
|
mutex_init(&isp->isp_mutex);
|
|
mutex_init(&isp->isp_mutex);
|
|
spin_lock_init(&isp->stat_lock);
|
|
spin_lock_init(&isp->stat_lock);
|
|
|
|
|
|
isp->dev = &pdev->dev;
|
|
isp->dev = &pdev->dev;
|
|
- isp->pdata = pdata;
|
|
|
|
isp->ref_count = 0;
|
|
isp->ref_count = 0;
|
|
|
|
|
|
ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32));
|
|
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;
|
|
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++)
|
|
for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++)
|
|
isp->mmio_base[i] =
|
|
isp->mmio_base[i] =
|
|
isp->mmio_base[0] + isp_res_maps[m].offset[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 =
|
|
isp->mmio_hist_base_phys =
|
|
mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST];
|
|
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 */
|
|
/* IOMMU */
|
|
ret = isp_attach_iommu(isp);
|
|
ret = isp_attach_iommu(isp);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
@@ -2394,6 +2579,9 @@ static int isp_probe(struct platform_device *pdev)
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto error_iommu;
|
|
goto error_iommu;
|
|
|
|
|
|
|
|
+ isp->notifier.bound = isp_subdev_notifier_bound;
|
|
|
|
+ isp->notifier.complete = isp_subdev_notifier_complete;
|
|
|
|
+
|
|
ret = isp_register_entities(isp);
|
|
ret = isp_register_entities(isp);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto error_modules;
|
|
goto error_modules;
|
|
@@ -2429,6 +2617,11 @@ static struct platform_device_id omap3isp_id_table[] = {
|
|
};
|
|
};
|
|
MODULE_DEVICE_TABLE(platform, 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 = {
|
|
static struct platform_driver omap3isp_driver = {
|
|
.probe = isp_probe,
|
|
.probe = isp_probe,
|
|
.remove = isp_remove,
|
|
.remove = isp_remove,
|
|
@@ -2436,6 +2629,7 @@ static struct platform_driver omap3isp_driver = {
|
|
.driver = {
|
|
.driver = {
|
|
.name = "omap3isp",
|
|
.name = "omap3isp",
|
|
.pm = &omap3isp_pm_ops,
|
|
.pm = &omap3isp_pm_ops,
|
|
|
|
+ .of_match_table = omap3isp_of_table,
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|