From patchwork Mon Dec 10 19:45:59 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylwester Nawrocki X-Patchwork-Id: 15852 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1Ti9JR-0002m0-4A for patchwork@linuxtv.org; Mon, 10 Dec 2012 20:46:49 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.75/mailfrontend-4) with esmtp for id 1Ti9JP-0005aS-Af; Mon, 10 Dec 2012 20:46:49 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752400Ab2LJTqn (ORCPT ); Mon, 10 Dec 2012 14:46:43 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:17329 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752338Ab2LJTqk (ORCPT ); Mon, 10 Dec 2012 14:46:40 -0500 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout3.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MET00KVFYX43HT0@mailout3.samsung.com>; Tue, 11 Dec 2012 04:46:39 +0900 (KST) X-AuditID: cbfee61a-b7fa66d0000004cf-e9-50c63c1fd5c2 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 8A.D1.01231.F1C36C05; Tue, 11 Dec 2012 04:46:39 +0900 (KST) Received: from amdc1344.digital.local ([106.116.147.32]) by mmp2.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0MET000K7YWW7YB0@mmp2.samsung.com>; Tue, 11 Dec 2012 04:46:39 +0900 (KST) From: Sylwester Nawrocki To: linux-media@vger.kernel.org Cc: g.liakhovetski@gmx.de, grant.likely@secretlab.ca, rob.herring@calxeda.com, thomas.abraham@linaro.org, t.figa@samsung.com, sw0312.kim@samsung.com, kyungmin.park@samsung.com, devicetree-discuss@lists.ozlabs.org, linux-samsung-soc@vger.kernel.org, Sylwester Nawrocki Subject: [PATCH RFC 05/12] s5p-fimc: Add device tree based sensors registration Date: Mon, 10 Dec 2012 20:45:59 +0100 Message-id: <1355168766-6068-6-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1355168766-6068-1-git-send-email-s.nawrocki@samsung.com> References: <1355168766-6068-1-git-send-email-s.nawrocki@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrOJMWRmVeSWpSXmKPExsVy+t9jQV15m2MBBucbtCx6NmxltZhxfh+T A5PH501yAYxRXDYpqTmZZalF+nYJXBkPT99iLWgJqvhxey9LA+M2xy5GTg4JAROJPZ/XskLY YhIX7q1n62Lk4hASmM4ocb/tHQuE08EkseP3NHaQKjYBQ4neo32MILaIgLzEk94bYB3MAquY JGbsewKWEBbwl+h+fhVsLIuAqkTvnzVgNq+Aq8ShU9uYuxg5gNYpSMyZZAMS5hRwk3i09hkT iC0EVPKy/xf7BEbeBYwMqxhFUwuSC4qT0nMN9YoTc4tL89L1kvNzNzGCvf9MagfjygaLQ4wC HIxKPLwaWscChFgTy4orcw8xSnAwK4nwRkgAhXhTEiurUovy44tKc1KLDzFKc7AoifM2e6QE CAmkJ5akZqemFqQWwWSZODilGhjr7vV8MPLyPP3qw/2vp5bYbJIr3RPLu6trvuTcxFOLl/2r 2PXYrLfVehn3T1dPMUvu9MzTF9Qnc3ccSA69nSv6vbz9d9x593Lp7UUTpzhw+OfuP8ToOPt5 8ZfCy8prb+eIZ+Yqi/k5PWnOeNEfcVDt3+XFBYe3/7P0PL4y5cfhQy0aCc3NlTFKLMUZiYZa zEXFiQBw+KaG+gEAAA== Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 5.6.1.2065439, Antispam-Engine: 2.7.2.376379, Antispam-Data: 2012.12.10.193317 X-PMX-Spam: Gauge=XIIII, Probability=14%, Report=' OBFUSCATING_HTML 1.2, MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __CP_NAME_BODY 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __SANE_MSGID 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS ' The sensor (I2C and/or SPI client) devices are instantiated by their corresponding control bus controllers. Since their master clock is often provided by a video bus receiver (host interface) or other than I2C/SPI controller device, the drivers of those client devices are not accessing hardware in their driver's probe() callback. Instead, after enabling clock, the host driver calls back into a sub-device when it wants to activate them. This pattern is used by some in-tree drivers and this patch also uses it for DT case. This patch is intended as a first step for adding device tree support to the S5P/Exynos SoC camera drivers. The second one is adding support for asynchronous sub-devices registration and clock control from sub-device driver level. The bindings shall not change when asynchronous probing support is added. The motivation behind this approach is to have basic support for device tree based platforms in the driver, while asynchronous subdev probing and related issues are being discussed on LMML. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park --- .../devicetree/bindings/media/soc/samsung-fimc.txt | 75 +++++++++ drivers/media/platform/s5p-fimc/fimc-mdevice.c | 168 +++++++++++++++++--- 2 files changed, 217 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/media/soc/samsung-fimc.txt b/Documentation/devicetree/bindings/media/soc/samsung-fimc.txt index 5bbda07..82bd619 100644 --- a/Documentation/devicetree/bindings/media/soc/samsung-fimc.txt +++ b/Documentation/devicetree/bindings/media/soc/samsung-fimc.txt @@ -73,6 +73,15 @@ node. Aliases are in form of fimc-lite, where is an integer (0...N) specifying the IP's instance index. +Image sensor nodes +------------------ + +The sensor device nodes should be added as their control bus controller +(e.g. I2C0) child nodes and linked to a port created under csis or +parallel-ports node, using common bindings for video input interfaces, +.i.e. port/endpoint node pairs. The implementation of this binding requires +at clock-frequency property to be present under sensor device nodes. + Example: aliases { @@ -80,6 +89,47 @@ Example: fimc0 = &fimc_0; }; + /* Parallel bus IF sensor */ + i2c_0: i2c@13860000 { + s5k6aa: sensor@3c { + compatible = "samsung,s5k6aafx"; + reg = <0x3c>; + vddio-supply = <...>; + + clock-frequency = <24000000>; + clocks = <...>; + clock-names = "mclk"; + + port { + s5k6aa_ep: endpoint { + remote-endpoint = <&fimc0_ep>; + bus-width = <8>; + hsync-active = <0>; + hsync-active = <1>; + pclk-sample = <1>; + }; + }; + }; + }; + + /* MIPI CSI-2 bus IF sensor */ + s5c73m3: sensor@0x1a { + compatible = "samsung,s5c73m3"; + reg = <0x1a>; + vddio-supply = <...>; + + clock-frequency = <24000000>; + clocks = <...>; + clock-names = "mclk"; + + port { + s5c73m3_1: endpoint { + data-lanes = <1>, <2>, <3>, <4>; + remote-endpoint = <&csis0_ep>; + }; + }; + }; + camera { compatible = "samsung,fimc", "simple-bus"; #address-cells = <1>; @@ -90,6 +140,21 @@ Example: pinctrl-0 = <&cam_port_a_clk_active>; pinctrl-1 = <&cam_port_a_clk_idle>; + /* parallel camera ports */ + parallel-ports { + /* camera A input */ + port@0 { + reg = <0>; + fimc0_ep: endpoint { + remote-endpoint = <&s5k6aa_ep>; + bus-width = <8>; + hsync-active = <0>; + hsync-active = <1>; + pclk-sample = <1>; + }; + }; + }; + fimc_0: fimc@11800000 { compatible = "samsung,exynos4210-fimc"; reg = <0x11800000 0x1000>; @@ -102,6 +167,16 @@ Example: reg = <0x11880000 0x1000>; interrupts = <0 78 0>; max-data-lanes = <4>; + /* camera C input */ + port { + reg = <2>; + csis0_ep: endpoint { + remote-endpoint = <&s5c73m3_ep>; + data-lanes = <1>, <2>, <3>, <4>; + samsung,csis-hs-settle = <12>; + samsung,camclk-out = <0>; + }; + }; }; }; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 2657e90..ee718af 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -19,11 +19,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include @@ -248,7 +252,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - s_info->pdata.board_info->type); + sd->name); return sd; } @@ -260,17 +264,122 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) if (!client) return; v4l2_device_unregister_subdev(sd); - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); + + if (!client->dev.of_node) { + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); + } +} + +static int fimc_md_of_add_sensor(struct fimc_md *fmd, + struct device_node *node, int index) +{ + struct fimc_sensor_info *si; + struct i2c_client *client; + struct v4l2_subdev *sd; + int ret; + + if (index >= ARRAY_SIZE(fmd->sensor)) + return -EINVAL; + si = &fmd->sensor[index]; + + client = of_find_i2c_device_by_node(node); + if (!client) + return -EPROBE_DEFER; + + device_lock(&client->dev); + + if (!client->driver || + !try_module_get(client->driver->driver.owner)) { + ret = -EAGAIN; + goto dev_put; + } + + /* Enable sensor's master clock */ + ret = __fimc_md_set_camclk(fmd, si, true); + if (ret < 0) + goto mod_put; + sd = i2c_get_clientdata(client); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + __fimc_md_set_camclk(fmd, si, false); + if (ret < 0) + goto mod_put; + + v4l2_set_subdev_hostdata(sd, si); + sd->grp_id = GRP_ID_SENSOR; + si->subdev = sd; + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", + sd->name, fmd->num_sensors); + fmd->num_sensors++; + +mod_put: + module_put(client->driver->driver.owner); +dev_put: + device_unlock(&client->dev); + put_device(&client->dev); + return ret; +} + +static int fimc_md_of_sensors_register(struct fimc_md *fmd, + struct device_node *np) +{ + struct s5p_fimc_isp_info *pd; + struct device_node *node; + int ret, index = 0; + + /* Attach sensors linked to MIPI CSI-2 receivers */ + for_each_available_child_of_node(fmd->pdev->dev.of_node, node) { + struct device_node *port, *remote, *endpoint; + u32 tmp; + + if (of_node_cmp(node->name, "csis")) + continue; + pd = &fmd->sensor[index].pdata; + + if (!(port = of_get_child_by_name(node, "port"))) + return -EINVAL; + if (of_property_read_u32(port, "reg", &tmp)) + return -EINVAL; + pd->mux_id = tmp & 0x1; + + if (!(endpoint = of_get_child_by_name(port, "endpoint"))) + return -EINVAL; + if (!of_property_read_u32(endpoint, "samsung,camclk-out", &tmp)) + pd->clk_id = tmp; + /* + * For MIPI CSI-2 we need only sensor's clock frequency + * from the remote endpoint node. + */ + remote = v4l2_of_get_remote(endpoint); + if (!remote) + return -EINVAL; + if (of_property_read_u32(remote, "clock-frequency", &tmp)) + return -EINVAL; + pd->clk_frequency = tmp; + + pd->bus_type = FIMC_MIPI_CSI2; + of_node_put(endpoint); + + ret = fimc_md_of_add_sensor(fmd, remote, index); + of_node_put(remote); + if (ret < 0) + break; + index++; + } + /* TODO: Parse parallel-ports node */ + return 0; } static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; + struct device_node *of_node = fmd->pdev->dev.of_node; struct fimc_dev *fd = NULL; - int num_clients, ret, i; + int num_clients = 0; + int ret, i; /* * Runtime resume one of the FIMC entities to make sure @@ -281,34 +390,41 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fd = fmd->fimc[i]; if (!fd) return -ENXIO; + ret = pm_runtime_get_sync(&fd->pdev->dev); if (ret < 0) return ret; - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); - - fmd->num_sensors = num_clients; - for (i = 0; i < num_clients; i++) { - struct v4l2_subdev *sd; + if (of_node) { + fmd->num_sensors = 0; + ret = fimc_md_of_sensors_register(fmd, of_node); + } else if (pdata) { + WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, + ARRAY_SIZE(fmd->sensor)); + fmd->num_sensors = num_clients; - fmd->sensor[i].pdata = pdata->isp_info[i]; - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; - if (!IS_ERR(sd)) { + fmd->sensor[i].pdata = pdata->isp_info[i]; + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); + if (ret) + break; + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (IS_ERR(sd)) { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } fmd->sensor[i].subdev = sd; - } else { - fmd->sensor[i].subdev = NULL; - ret = PTR_ERR(sd); - break; + if (ret) + break; } - if (ret) - break; } + pm_runtime_put(&fd->pdev->dev); return ret; } @@ -995,7 +1111,7 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_unlock; - if (pdev->dev.platform_data) { + if (pdev->dev.platform_data || pdev->dev.of_node) { ret = fimc_md_register_sensor_entities(fmd); if (ret) goto err_unlock;