aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/omap3isp/isp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/omap3isp/isp.c')
-rw-r--r--drivers/media/platform/omap3isp/isp.c563
1 files changed, 393 insertions, 170 deletions
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index deca80903c3a..18d0a871747f 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -51,6 +51,7 @@
#include <linux/dma-mapping.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/platform_device.h>
@@ -63,6 +64,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
#include "isp.h"
#include "ispreg.h"
@@ -85,35 +87,45 @@ static void isp_restore_ctx(struct isp_device *isp);
static const struct isp_res_mapping isp_res_maps[] = {
{
.isp_rev = ISP_REVISION_2_0,
- .map = 1 << OMAP3_ISP_IOMEM_MAIN |
- 1 << OMAP3_ISP_IOMEM_CCP2 |
- 1 << OMAP3_ISP_IOMEM_CCDC |
- 1 << OMAP3_ISP_IOMEM_HIST |
- 1 << OMAP3_ISP_IOMEM_H3A |
- 1 << OMAP3_ISP_IOMEM_PREV |
- 1 << OMAP3_ISP_IOMEM_RESZ |
- 1 << OMAP3_ISP_IOMEM_SBL |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
- 1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
+ .offset = {
+ /* first MMIO area */
+ 0x0000, /* base, len 0x0070 */
+ 0x0400, /* ccp2, len 0x01f0 */
+ 0x0600, /* ccdc, len 0x00a8 */
+ 0x0a00, /* hist, len 0x0048 */
+ 0x0c00, /* h3a, len 0x0060 */
+ 0x0e00, /* preview, len 0x00a0 */
+ 0x1000, /* resizer, len 0x00ac */
+ 0x1200, /* sbl, len 0x00fc */
+ /* second MMIO area */
+ 0x0000, /* csi2a, len 0x0170 */
+ 0x0170, /* csiphy2, len 0x000c */
+ },
+ .syscon_offset = 0xdc,
+ .phy_type = ISP_PHY_TYPE_3430,
},
{
.isp_rev = ISP_REVISION_15_0,
- .map = 1 << OMAP3_ISP_IOMEM_MAIN |
- 1 << OMAP3_ISP_IOMEM_CCP2 |
- 1 << OMAP3_ISP_IOMEM_CCDC |
- 1 << OMAP3_ISP_IOMEM_HIST |
- 1 << OMAP3_ISP_IOMEM_H3A |
- 1 << OMAP3_ISP_IOMEM_PREV |
- 1 << OMAP3_ISP_IOMEM_RESZ |
- 1 << OMAP3_ISP_IOMEM_SBL |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
- 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY1 |
- 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 |
- 1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
+ .offset = {
+ /* first MMIO area */
+ 0x0000, /* base, len 0x0070 */
+ 0x0400, /* ccp2, len 0x01f0 */
+ 0x0600, /* ccdc, len 0x00a8 */
+ 0x0a00, /* hist, len 0x0048 */
+ 0x0c00, /* h3a, len 0x0060 */
+ 0x0e00, /* preview, len 0x00a0 */
+ 0x1000, /* resizer, len 0x00ac */
+ 0x1200, /* sbl, len 0x00fc */
+ /* second MMIO area */
+ 0x0000, /* csi2a, len 0x0170 (1st area) */
+ 0x0170, /* csiphy2, len 0x000c */
+ 0x01c0, /* csi2a, len 0x0040 (2nd area) */
+ 0x0400, /* csi2c, len 0x0170 (1st area) */
+ 0x0570, /* csiphy1, len 0x000c */
+ 0x05c0, /* csi2c, len 0x0040 (2nd area) */
+ },
+ .syscon_offset = 0x2f0,
+ .phy_type = ISP_PHY_TYPE_3630,
},
};
@@ -279,9 +291,20 @@ static const struct clk_init_data isp_xclk_init_data = {
.num_parents = 1,
};
+static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data)
+{
+ unsigned int idx = clkspec->args[0];
+ struct isp_device *isp = data;
+
+ if (idx >= ARRAY_SIZE(isp->xclks))
+ return ERR_PTR(-ENOENT);
+
+ return isp->xclks[idx].clk;
+}
+
static int isp_xclk_init(struct isp_device *isp)
{
- struct isp_platform_data *pdata = isp->pdata;
+ struct device_node *np = isp->dev->of_node;
struct clk_init_data init;
unsigned int i;
@@ -311,37 +334,27 @@ static int isp_xclk_init(struct isp_device *isp)
xclk->clk = clk_register(NULL, &xclk->hw);
if (IS_ERR(xclk->clk))
return PTR_ERR(xclk->clk);
-
- if (pdata->xclks[i].con_id == NULL &&
- pdata->xclks[i].dev_id == NULL)
- continue;
-
- xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
- if (xclk->lookup == NULL)
- return -ENOMEM;
-
- xclk->lookup->con_id = pdata->xclks[i].con_id;
- xclk->lookup->dev_id = pdata->xclks[i].dev_id;
- xclk->lookup->clk = xclk->clk;
-
- clkdev_add(xclk->lookup);
}
+ if (np)
+ of_clk_add_provider(np, isp_xclk_src_get, isp);
+
return 0;
}
static void isp_xclk_cleanup(struct isp_device *isp)
{
+ struct device_node *np = isp->dev->of_node;
unsigned int i;
+ if (np)
+ of_clk_del_provider(np);
+
for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
struct isp_xclk *xclk = &isp->xclks[i];
if (!IS_ERR(xclk->clk))
clk_unregister(xclk->clk);
-
- if (xclk->lookup)
- clkdev_drop(xclk->lookup);
}
}
@@ -422,7 +435,7 @@ static void isp_core_init(struct isp_device *isp, int idle)
*/
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata,
+ const struct isp_parallel_cfg *parcfg,
unsigned int shift, unsigned int bridge)
{
u32 ispctrl_val;
@@ -437,8 +450,8 @@ void omap3isp_configure_bridge(struct isp_device *isp,
switch (input) {
case CCDC_INPUT_PARALLEL:
ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
- ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
- shift += pdata->data_lane_shift * 2;
+ ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+ shift += parcfg->data_lane_shift * 2;
break;
case CCDC_INPUT_CSI2A:
@@ -1784,58 +1797,121 @@ static void isp_unregister_entities(struct isp_device *isp)
}
/*
- * isp_register_subdev_group - Register a group of subdevices
+ * isp_register_subdev - Register a sub-device
* @isp: OMAP3 ISP device
- * @board_info: I2C subdevs board information array
+ * @isp_subdev: platform data related to a sub-device
*
- * Register all I2C subdevices in the board_info array. The array must be
- * terminated by a NULL entry, and the first entry must be the sensor.
+ * Register an I2C sub-device which has not been registered by other
+ * means (such as the Device Tree).
*
- * Return a pointer to the sensor media entity if it has been successfully
+ * Return a pointer to the sub-device if it has been successfully
* registered, or NULL otherwise.
*/
static struct v4l2_subdev *
-isp_register_subdev_group(struct isp_device *isp,
- struct isp_subdev_i2c_board_info *board_info)
+isp_register_subdev(struct isp_device *isp,
+ struct isp_platform_subdev *isp_subdev)
{
- struct v4l2_subdev *sensor = NULL;
- unsigned int first;
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd;
- if (board_info->board_info == NULL)
+ if (isp_subdev->board_info == NULL)
return NULL;
- for (first = 1; board_info->board_info; ++board_info, first = 0) {
- struct v4l2_subdev *subdev;
- struct i2c_adapter *adapter;
+ adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id);
+ if (adapter == NULL) {
+ dev_err(isp->dev,
+ "%s: Unable to get I2C adapter %d for device %s\n",
+ __func__, isp_subdev->i2c_adapter_id,
+ isp_subdev->board_info->type);
+ return NULL;
+ }
- adapter = i2c_get_adapter(board_info->i2c_adapter_id);
- if (adapter == NULL) {
- dev_err(isp->dev, "%s: Unable to get I2C adapter %d for "
- "device %s\n", __func__,
- board_info->i2c_adapter_id,
- board_info->board_info->type);
- continue;
- }
+ sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+ isp_subdev->board_info, NULL);
+ if (sd == NULL) {
+ dev_err(isp->dev, "%s: Unable to register subdev %s\n",
+ __func__, isp_subdev->board_info->type);
+ return NULL;
+ }
- subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
- board_info->board_info, NULL);
- if (subdev == NULL) {
- dev_err(isp->dev, "%s: Unable to register subdev %s\n",
- __func__, board_info->board_info->type);
- continue;
- }
+ return sd;
+}
+
+static int isp_link_entity(
+ struct isp_device *isp, struct media_entity *entity,
+ enum isp_interface_type interface)
+{
+ struct media_entity *input;
+ unsigned int flags;
+ unsigned int pad;
+ unsigned int i;
+
+ /* Connect the sensor to the correct interface module.
+ * Parallel sensors are connected directly to the CCDC, while
+ * serial sensors are connected to the CSI2a, CCP2b or CSI2c
+ * receiver through CSIPHY1 or CSIPHY2.
+ */
+ switch (interface) {
+ case ISP_INTERFACE_PARALLEL:
+ input = &isp->isp_ccdc.subdev.entity;
+ pad = CCDC_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2A_PHY2:
+ input = &isp->isp_csi2a.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ case ISP_INTERFACE_CCP2B_PHY1:
+ case ISP_INTERFACE_CCP2B_PHY2:
+ input = &isp->isp_ccp2.subdev.entity;
+ pad = CCP2_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2C_PHY1:
+ input = &isp->isp_csi2c.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ break;
- if (first)
- sensor = subdev;
+ default:
+ dev_err(isp->dev, "%s: invalid interface type %u\n", __func__,
+ interface);
+ return -EINVAL;
+ }
+
+ /*
+ * Not all interfaces are available on all revisions of the
+ * ISP. The sub-devices of those interfaces aren't initialised
+ * in such a case. Check this by ensuring the num_pads is
+ * non-zero.
+ */
+ if (!input->num_pads) {
+ dev_err(isp->dev, "%s: invalid input %u\n", entity->name,
+ interface);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < entity->num_pads; i++) {
+ if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+ if (i == entity->num_pads) {
+ dev_err(isp->dev, "%s: no source pad in external entity\n",
+ __func__);
+ return -EINVAL;
}
- return sensor;
+ return media_entity_create_link(entity, i, input, pad, flags);
}
static int isp_register_entities(struct isp_device *isp)
{
struct isp_platform_data *pdata = isp->pdata;
- struct isp_v4l2_subdevs_group *subdevs;
+ struct isp_platform_subdev *isp_subdev;
int ret;
isp->media_dev.dev = isp->dev;
@@ -1892,74 +1968,31 @@ 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 (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
- struct v4l2_subdev *sensor;
- struct media_entity *input;
- unsigned int flags;
- unsigned int pad;
- unsigned int i;
-
- sensor = isp_register_subdev_group(isp, subdevs->subdevs);
- if (sensor == NULL)
- continue;
+ for (isp_subdev = pdata ? pdata->subdevs : NULL;
+ isp_subdev && isp_subdev->board_info; isp_subdev++) {
+ struct v4l2_subdev *sd;
- sensor->host_priv = subdevs;
+ sd = isp_register_subdev(isp, isp_subdev);
- /* Connect the sensor to the correct interface module. Parallel
- * sensors are connected directly to the CCDC, while serial
- * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
- * through CSIPHY1 or CSIPHY2.
+ /*
+ * No bus information --- this is either a flash or a
+ * lens subdev.
*/
- switch (subdevs->interface) {
- case ISP_INTERFACE_PARALLEL:
- input = &isp->isp_ccdc.subdev.entity;
- pad = CCDC_PAD_SINK;
- flags = 0;
- break;
-
- case ISP_INTERFACE_CSI2A_PHY2:
- input = &isp->isp_csi2a.subdev.entity;
- pad = CSI2_PAD_SINK;
- flags = MEDIA_LNK_FL_IMMUTABLE
- | MEDIA_LNK_FL_ENABLED;
- break;
-
- case ISP_INTERFACE_CCP2B_PHY1:
- case ISP_INTERFACE_CCP2B_PHY2:
- input = &isp->isp_ccp2.subdev.entity;
- pad = CCP2_PAD_SINK;
- flags = 0;
- break;
-
- case ISP_INTERFACE_CSI2C_PHY1:
- input = &isp->isp_csi2c.subdev.entity;
- pad = CSI2_PAD_SINK;
- flags = MEDIA_LNK_FL_IMMUTABLE
- | MEDIA_LNK_FL_ENABLED;
- break;
-
- default:
- dev_err(isp->dev, "%s: invalid interface type %u\n",
- __func__, subdevs->interface);
- ret = -EINVAL;
- goto done;
- }
+ if (!sd || !isp_subdev->bus)
+ continue;
- for (i = 0; i < sensor->entity.num_pads; i++) {
- if (sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
- break;
- }
- if (i == sensor->entity.num_pads) {
- dev_err(isp->dev,
- "%s: no source pad in external entity\n",
- __func__);
- ret = -EINVAL;
- goto done;
- }
+ sd->host_priv = isp_subdev->bus;
- ret = media_entity_create_link(&sensor->entity, i, input, pad,
- flags);
+ ret = isp_link_entity(isp, &sd->entity,
+ isp_subdev->bus->interface);
if (ret < 0)
goto done;
}
@@ -1967,8 +2000,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;
}
@@ -2183,6 +2218,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);
@@ -2194,26 +2230,156 @@ static int isp_remove(struct platform_device *pdev)
return 0;
}
-static int isp_map_mem_resource(struct platform_device *pdev,
- struct isp_device *isp,
- enum isp_mem_resources res)
+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 resource *mem;
+ struct isp_bus_cfg *buscfg = &isd->bus;
+ struct v4l2_of_endpoint vep;
+ unsigned int i;
- /* request the mem region for the camera registers */
+ 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;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+ 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);
+ }
- /* map the region */
- isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem);
- if (IS_ERR(isp->mmio_base[res]))
- return PTR_ERR(isp->mmio_base[res]);
+ /*
+ * 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;
- isp->mmio_base_phys[res] = mem->start;
+ 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
@@ -2227,47 +2393,86 @@ static int isp_map_mem_resource(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;
int i, m;
- if (pdata == NULL)
- return -EINVAL;
-
isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp) {
dev_err(&pdev->dev, "could not allocate memory\n");
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);
+ dev_warn(&pdev->dev,
+ "Platform data support is deprecated! Please move to DT now!\n");
+ }
+
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));
if (ret)
- return ret;
+ goto error;
platform_set_drvdata(pdev, isp);
/* Regulators */
- isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY1");
- isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY2");
+ isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1");
+ isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2");
/* Clocks
*
* The ISP clock tree is revision-dependent. We thus need to enable ICLK
* manually to read the revision before calling __omap3isp_get().
+ *
+ * Start by mapping the ISP MMIO area, which is in two pieces.
+ * The ISP IOMMU is in between. Map both now, and fill in the
+ * ISP revision specific portions a little later in the
+ * function.
*/
- ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
- if (ret < 0)
- goto error;
+ for (i = 0; i < 2; i++) {
+ unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ isp->mmio_base[map_idx] =
+ devm_ioremap_resource(isp->dev, mem);
+ if (IS_ERR(isp->mmio_base[map_idx]))
+ return PTR_ERR(isp->mmio_base[map_idx]);
+ }
ret = isp_get_clocks(isp);
if (ret < 0)
@@ -2308,14 +2513,23 @@ static int isp_probe(struct platform_device *pdev)
goto error_isp;
}
- for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp_res_maps[m].map & 1 << i) {
- ret = isp_map_mem_resource(pdev, isp, i);
- if (ret)
- 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];
+
+ for (i = OMAP3_ISP_IOMEM_CSIPHY2; i < OMAP3_ISP_IOMEM_LAST; i++)
+ isp->mmio_base[i] =
+ isp->mmio_base[OMAP3_ISP_IOMEM_CSI2A_REGS1]
+ + isp_res_maps[m].offset[i];
+
+ isp->mmio_hist_base_phys =
+ mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST];
+
/* IOMMU */
ret = isp_attach_iommu(isp);
if (ret < 0) {
@@ -2343,6 +2557,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;
@@ -2378,6 +2595,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,
@@ -2385,6 +2607,7 @@ static struct platform_driver omap3isp_driver = {
.driver = {
.name = "omap3isp",
.pm = &omap3isp_pm_ops,
+ .of_match_table = omap3isp_of_table,
},
};