From patchwork Mon Mar 16 00:26:09 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sakari Ailus X-Patchwork-Id: 6014821 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id E0633BF90F for ; Mon, 16 Mar 2015 00:27:41 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A0A37202F0 for ; Mon, 16 Mar 2015 00:27:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 150B920306 for ; Mon, 16 Mar 2015 00:27:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753036AbbCPA1V (ORCPT ); Sun, 15 Mar 2015 20:27:21 -0400 Received: from nblzone-211-213.nblnetworks.fi ([83.145.211.213]:45112 "EHLO hillosipuli.retiisi.org.uk" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752680AbbCPA1B (ORCPT ); Sun, 15 Mar 2015 20:27:01 -0400 Received: from lanttu.localdomain (lanttu.localdomain [192.168.5.64]) by hillosipuli.retiisi.org.uk (Postfix) with ESMTP id E8E0C600A8; Mon, 16 Mar 2015 02:26:56 +0200 (EET) From: Sakari Ailus To: linux-media@vger.kernel.org Cc: linux-omap@vger.kernel.org, tony@atomide.com, sre@kernel.org, pali.rohar@gmail.com, laurent.pinchart@ideasonboard.com Subject: [PATCH 14/15] omap3isp: Add support for the Device Tree Date: Mon, 16 Mar 2015 02:26:09 +0200 Message-Id: <1426465570-30295-15-git-send-email-sakari.ailus@iki.fi> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1426465570-30295-1-git-send-email-sakari.ailus@iki.fi> References: <1426465570-30295-1-git-send-email-sakari.ailus@iki.fi> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- drivers/media/platform/omap3isp/isp.c | 209 +++++++++++++++++++++++++-- drivers/media/platform/omap3isp/isp.h | 11 ++ drivers/media/platform/omap3isp/ispcsiphy.c | 7 + 3 files changed, 215 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 992e74c..0d6012a 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -64,6 +64,7 @@ #include #include +#include #include "isp.h" #include "ispreg.h" @@ -1991,6 +1992,14 @@ static int isp_register_entities(struct isp_device *isp) if (ret < 0) goto done; + /* + * Device Tree --- the external of the sub-devices will be + * registered later. 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 +2025,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 +2243,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 +2255,151 @@ 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, "interface %u\n", 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_polarity[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_polarity[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, "invalid interface %d\n\n", 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; + struct v4l2_async_subdev **asds; + + asds = notifier->subdevs = + devm_kcalloc(dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), + GFP_KERNEL); + if (!asds) + return -ENOMEM; + + while ((node = of_graph_get_next_endpoint(dev->of_node, node)) && + notifier->num_subdevs < ISP_MAX_SUBDEVS) { + struct isp_async_subdev *isd; + + isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL); + if (!isd) + return -ENOMEM; + + *asds = &isd->asd; + + if (isp_of_parse_node(dev, node, isd)) + return -EINVAL; + + isd->asd.match.of.node = of_graph_get_remote_port_parent(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 rval; + + rval = isp_link_entity(isp, &subdev->entity, isd->bus.interface); + if (rval < 0) + return rval; + + isd->sd = subdev; + isd->sd->host_priv = &isd->bus; + + return rval; +} + +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 +2413,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 +2424,37 @@ 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 = 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 +2526,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 +2543,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 +2570,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 +2608,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 +2620,7 @@ static struct platform_driver omap3isp_driver = { .driver = { .name = "omap3isp", .pm = &omap3isp_pm_ops, + .of_match_table = omap3isp_of_table, }, }; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index dcb7d20..431224e 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -18,6 +18,7 @@ #define OMAP3_ISP_CORE_H #include +#include #include #include #include @@ -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) \ diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index d91dde1..495447d 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -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;