From patchwork Wed Aug 28 15:55:58 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylwester Nawrocki X-Patchwork-Id: 19940 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1VEi7U-0007jC-6V; Wed, 28 Aug 2013 17:57:20 +0200 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.72/mailfrontend-6) with esmtp id 1VEi7R-0004H5-69; Wed, 28 Aug 2013 17:57:20 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754462Ab3H1P5P (ORCPT + 1 other); Wed, 28 Aug 2013 11:57:15 -0400 Received: from mailout1.samsung.com ([203.254.224.24]:34150 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754504Ab3H1P5O (ORCPT ); Wed, 28 Aug 2013 11:57:14 -0400 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MS9004EV0AX0NN0@mailout1.samsung.com> for linux-media@vger.kernel.org; Thu, 29 Aug 2013 00:57:12 +0900 (KST) X-AuditID: cbfee61a-b7f7a6d00000235f-e1-521e1dd85324 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id DF.39.09055.8DD1E125; Thu, 29 Aug 2013 00:57:12 +0900 (KST) Received: from amdc1344.digital.local ([106.116.147.32]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0MS900LE309EAJB0@mmp1.samsung.com>; Thu, 29 Aug 2013 00:57:12 +0900 (KST) From: Sylwester Nawrocki To: linux-media@vger.kernel.org Cc: mturquette@linaro.org, g.liakhovetski@gmx.de, laurent.pinchart@ideasonboard.com, arun.kk@samsung.com, hverkuil@xs4all.nl, sakari.ailus@iki.fi, a.hajda@samsung.com, kyungmin.park@samsung.com, t.figa@samsung.com, linux-arm-kernel@lists.infradead.org, Sylwester Nawrocki Subject: [PATCH v2 5/7] exynos4-is: Add clock provider for the external clocks Date: Wed, 28 Aug 2013 17:55:58 +0200 Message-id: <1377705360-12197-6-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1377705360-12197-1-git-send-email-s.nawrocki@samsung.com> References: <1377705360-12197-1-git-send-email-s.nawrocki@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrJLMWRmVeSWpSXmKPExsVy+t9jAd0bsnJBBteOGVrcWneO1eLjqdus Fu83zmOyODX5GZPF2aY37BadE5ewW2x6fI3VomfDVlaLpxMuslkcftPOanFm/0o2i/UzXrM4 8Hh8+BjnMbtjJqvH4a8LWTzuXNvD5rF5Sb1H35ZVjB6fN8l5nPr6mT2AI4rLJiU1J7MstUjf LoErY2HrApaC8w4VG859ZGpgvGjSxcjJISFgItG86BwThC0mceHeerYuRi4OIYFFjBIzFq1i hXA6mCQmdpxkBKliEzCU6D3aB2aLCMhLPOm9AdbBLLCBSeLuinvsIAlhAT+Jf986mEFsFgFV iRuL1oA18Aq4SZxb8QNoKgfQOgWJOZNsQExOAXeJOxckQCqEgCoe3W5mm8DIu4CRYRWjaGpB ckFxUnquoV5xYm5xaV66XnJ+7iZGcIA+k9rBuLLB4hCjAAejEg9vxG/ZICHWxLLiytxDjBIc zEoivL9F5IKEeFMSK6tSi/Lji0pzUosPMUpzsCiJ8x5otQ4UEkhPLEnNTk0tSC2CyTJxcEo1 MC6ItK2o3XmE10gtaFJdd5xcFlvKo40rZhU8O/r4wD6mGuWK7Zeq73B4bvw9Y1nr9Uvz27iv XHp31pj73y+J6CmXM5wWbmNpzbxWL8V1YVNuWLj5zUvcTuKT+Dziyr7Pioxtk34cy9ORIJEz ucM3gG/Bao8p/yc9+ibBqyVsmj4rQjs/4cNUYSWW4oxEQy3mouJEALul4etMAgAA Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2013.8.28.154814 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, URI_ENDS_IN_HTML 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __CP_URI_IN_BODY 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __IN_REP_TO 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_WWW 0, __URI_NS ' This patch adds clock provider to expose the sclk_cam0/1 clocks for image sensor subdevs. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park --- .../devicetree/bindings/media/samsung-fimc.txt | 17 ++- drivers/media/platform/exynos4-is/media-dev.c | 115 ++++++++++++++++++++ drivers/media/platform/exynos4-is/media-dev.h | 18 ++- 3 files changed, 147 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt index 96312f6..9f4d295 100644 --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt @@ -91,6 +91,15 @@ Optional properties - samsung,camclk-out : specifies clock output for remote sensor, 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT; +'clock-controller' node (optional) +---------------------------------- + +The purpose of this node is to define a clock provider for external image +sensors and link any of the CAM_?_CLKOUT clock outputs with related external +clock consumer device. Properties specific to this node are described in +../clock/clock-bindings.txt. + + Image sensor nodes ------------------ @@ -114,7 +123,7 @@ Example: vddio-supply = <...>; clock-frequency = <24000000>; - clocks = <...>; + clocks = <&camclk 1>; clock-names = "mclk"; port { @@ -135,7 +144,7 @@ Example: vddio-supply = <...>; clock-frequency = <24000000>; - clocks = <...>; + clocks = <&camclk 0>; clock-names = "mclk"; port { @@ -156,6 +165,10 @@ Example: pinctrl-names = "default"; pinctrl-0 = <&cam_port_a_clk_active>; + camclk: clock-controller { + #clock-cells = <1>; + }; + /* parallel camera ports */ parallel-ports { /* camera A input */ diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e327f45..6fba5f6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include #include @@ -1438,6 +1440,108 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) return 0; } +#ifdef CONFIG_OF +static int cam_clk_prepare(struct clk_hw *hw) +{ + struct cam_clk *camclk = to_cam_clk(hw); + int ret; + + if (camclk->fmd->pmf == NULL) + return -ENODEV; + + ret = pm_runtime_get_sync(camclk->fmd->pmf); + return ret < 0 ? ret : 0; +} + +static void cam_clk_unprepare(struct clk_hw *hw) +{ + struct cam_clk *camclk = to_cam_clk(hw); + + if (camclk->fmd->pmf == NULL) + return; + + pm_runtime_put_sync(camclk->fmd->pmf); +} + +static const struct clk_ops cam_clk_ops = { + .prepare = cam_clk_prepare, + .unprepare = cam_clk_unprepare, +}; + +static const char *cam_clk_p_names[] = { "sclk_cam0", "sclk_cam1" }; + +static void fimc_md_unregister_clk_provider(struct fimc_md *fmd) +{ + struct cam_clk_provider *cp = &fmd->clk_provider; + unsigned int i; + + if (cp->of_node) + of_clk_del_provider(cp->of_node); + + for (i = 0; i < ARRAY_SIZE(cp->clks); i++) + if (!IS_ERR(cp->clks[i])) + clk_unregister(cp->clks[i]); +} + +static int fimc_md_register_clk_provider(struct fimc_md *fmd) +{ + struct cam_clk_provider *cp = &fmd->clk_provider; + struct device *dev = &fmd->pdev->dev; + struct device_node *node; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(cp->clks); i++) + cp->clks[i] = ERR_PTR(-EINVAL); + + node = of_get_child_by_name(dev->of_node, "clock-controller"); + if (!node) { + dev_warn(dev, "clock-controller node at %s not found\n", + dev->of_node->full_name); + return 0; + } + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { + struct cam_clk *camclk = &cp->camclk[i]; + struct clk_init_data init; + char clk_name[16]; + struct clk *clk; + + snprintf(clk_name, sizeof(clk_name), "cam_clkout%d", i); + + init.name = clk_name; + init.ops = &cam_clk_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &cam_clk_p_names[i]; + init.num_parents = 1; + camclk->hw.init = &init; + camclk->fmd = fmd; + + clk = clk_register(dev, &camclk->hw); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register clock: %s (%ld)\n", + clk_name, PTR_ERR(clk)); + ret = PTR_ERR(clk); + goto err; + } + cp->clks[i] = clk; + } + + cp->clk_data.clks = cp->clks; + cp->clk_data.clk_num = i; + cp->of_node = node; + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, + &cp->clk_data); + if (!ret) + return 0; +err: + fimc_md_unregister_clk_provider(fmd); + return ret; +} +#else +#define fimc_md_register_clk_provider(fmd) (0) +#define fimc_md_unregister_clk_provider(fmd) (0) +#endif + static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1465,16 +1569,24 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->use_isp = fimc_md_is_isp_available(dev->of_node); + ret = fimc_md_register_clk_provider(fmd); + if (ret < 0) { + v4l2_err(v4l2_dev, "clock provider registration failed\n"); + return ret; + } + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; } + ret = media_device_register(&fmd->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); goto err_md; } + ret = fimc_md_get_clocks(fmd); if (ret) goto err_clk; @@ -1508,6 +1620,7 @@ static int fimc_md_probe(struct platform_device *pdev) ret = fimc_md_create_links(fmd); if (ret) goto err_unlock; + ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret) goto err_unlock; @@ -1528,6 +1641,7 @@ err_clk: media_device_unregister(&fmd->media_dev); err_md: v4l2_device_unregister(&fmd->v4l2_dev); + fimc_md_unregister_clk_provider(fmd); return ret; } @@ -1538,6 +1652,7 @@ static int fimc_md_remove(struct platform_device *pdev) if (!fmd) return 0; + fimc_md_unregister_clk_provider(fmd); v4l2_device_unregister(&fmd->v4l2_dev); device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); fimc_md_unregister_entities(fmd); diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 62599fd..240ca71 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -10,6 +10,7 @@ #define FIMC_MDEVICE_H_ #include +#include #include #include #include @@ -89,6 +90,12 @@ struct fimc_sensor_info { struct fimc_dev *host; }; +struct cam_clk { + struct clk_hw hw; + struct fimc_md *fmd; +}; +#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw) + /** * struct fimc_md - fimc media device information * @csis: MIPI CSIS subdevs data @@ -105,6 +112,7 @@ struct fimc_sensor_info { * @pinctrl: camera port pinctrl handle * @state_default: pinctrl default state handle * @state_idle: pinctrl idle state handle + * @cam_clk_provider: CAMCLK clock provider structure * @user_subdev_api: true if subdevs are not configured by the host driver * @slock: spinlock protecting @sensor array */ @@ -122,13 +130,21 @@ struct fimc_md { struct media_device media_dev; struct v4l2_device v4l2_dev; struct platform_device *pdev; + struct fimc_pinctrl { struct pinctrl *pinctrl; struct pinctrl_state *state_default; struct pinctrl_state *state_idle; } pinctl; - bool user_subdev_api; + struct cam_clk_provider { + struct clk *clks[FIMC_MAX_CAMCLKS]; + struct clk_onecell_data clk_data; + struct device_node *of_node; + struct cam_clk camclk[FIMC_MAX_CAMCLKS]; + } clk_provider; + + bool user_subdev_api; spinlock_t slock; struct list_head pipelines; };