From patchwork Thu Aug 29 09:24:32 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sylwester Nawrocki/Kernel \\(PLT\\) /SRPOL/Staff Engineer/Samsung Electronics" X-Patchwork-Id: 2851176 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 66FEDBF546 for ; Thu, 29 Aug 2013 09:26:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id ABA80202CC for ; Thu, 29 Aug 2013 09:26:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E27CC201D5 for ; Thu, 29 Aug 2013 09:26:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756038Ab3H2J0p (ORCPT ); Thu, 29 Aug 2013 05:26:45 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:44150 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753296Ab3H2J0n (ORCPT ); Thu, 29 Aug 2013 05:26:43 -0400 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout2.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MSA00DPYCWEM2J0@mailout2.samsung.com>; Thu, 29 Aug 2013 18:26:42 +0900 (KST) X-AuditID: cbfee61a-b7f7a6d00000235f-10-521f13d17844 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 10.AE.09055.1D31F125; Thu, 29 Aug 2013 18:26:42 +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 <0MSA00IQYCT4W430@mmp2.samsung.com>; Thu, 29 Aug 2013 18:26:41 +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, mark.rutland@arm.com, swarren@wwwdotorg.org, pawel.moll@arm.com, rob.herring@calxeda.com, galak@codeaurora.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, Sylwester Nawrocki Subject: [RESEND PATCH v2 1/7] V4L: s5c73m3: Add device tree support Date: Thu, 29 Aug 2013 11:24:32 +0200 Message-id: <1377768278-15391-2-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1377768278-15391-1-git-send-email-s.nawrocki@samsung.com> References: <1377768278-15391-1-git-send-email-s.nawrocki@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrPLMWRmVeSWpSXmKPExsVy+t9jQd1LwvJBBrtOs1vcWneO1eLjqdus FvOPAFnvN85jsuh/s5DV4tTkZ0wWZ5vesFt0TlzCbrHp8TVWi54NW1ktZpzfx2Sx9PpFJoun Ey6yWUyYvpbF4vCKA0wWh9+0s1qc2b+SzeLVwTYWi/UzXrM4CHusmbeG0WPB5yvsHpf7epk8 PnyM85jdMZPV4/DXhSwed67tYfPYvKTeo2/LKkaPz5vkPDbODfU49fUzewBPFJdNSmpOZllq kb5dAlfGrp6pTAXraysO3ZnI0sD4LLWLkZNDQsBE4sTxP+wQtpjEhXvr2boYuTiEBKYzStz7 +4gdwulgknh1/QULSBWbgKFE79E+RhBbREBe4knvDbAOZoHHzBLzmp+ygSSEBVwkbt29AWaz CKhK/L03lbWLkYODV8BN4vQUZRBTQkBBYs4kG5AKTgF3ic3TDoIdIQRUsX/pdvYJjLwLGBlW MYqmFiQXFCel5xrqFSfmFpfmpesl5+duYgRHwzOpHYwrGywOMQpwMCrx8Eb8lg0SYk0sK67M PcQowcGsJML7llM+SIg3JbGyKrUoP76oNCe1+BCjNAeLkjjvgVbrQCGB9MSS1OzU1ILUIpgs EwenVAOjyokbNretZ3FYNy1vaN627V01y0/R6ZxRFvPuLGj2yZss8+7SEp4rv++yPluw9O10 n7o/EhvXyQnaL1T9O5llavvyqxfX/63buo7hw8+Fx4QUefcyGRdNlTu5+NmFvCyTeX3ngw/O U4y4cutKkNSVu3/n//JY6enWz/2Qh/l0qn3Kcb/D2nNv7FNiKc5INNRiLipOBAC4eyyYggIA AA== Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-9.4 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, 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 From: Andrzej Hajda This patch adds the V4L2 asychronous subdev registration and device tree support. Common clock API is used to control the sensor master clock from within the subdev driver. Signed-off-by: Andrzej Hajda Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park --- Changes since v1: - added missing ret assignment on clk_get failure, - fixed error paths in probe(), - changed clock name to cis_extclk as specified in the datasheet, - separate properties used for the XSHUTDOWN, STANDBY GPIOs, - multiple correctiond in the binding documentation (improved description of data-lanes and clock-frequency properties). --- .../devicetree/bindings/media/samsung-s5c73m3.txt | 95 +++++++++ drivers/media/i2c/s5c73m3/s5c73m3-core.c | 206 +++++++++++++++----- drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 + drivers/media/i2c/s5c73m3/s5c73m3.h | 4 + 4 files changed, 261 insertions(+), 50 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/samsung-s5c73m3.txt -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt new file mode 100644 index 0000000..6d6c0b6 --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt @@ -0,0 +1,95 @@ +Samsung S5C73M3 8Mp camera ISP +------------------------------ + +The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video +data busses. The I2C bus is the main control bus and additionally the SPI bus +is used, mostly for transferring the firmware to and from the device. Two +slave device nodes corresponding to these control bus interfaces are required +and should be placed under respective bus controller nodes. + +I2C slave device node +--------------------- + +Required properties: + +- compatible : "samsung,s5c73m3"; +- reg : I2C slave address of the sensor; +- vdd-int-supply : digital power supply (1.2V); +- vdda-supply : analog power supply (1.2V); +- vdd-reg-supply : regulator input power supply (2.8V); +- vddio-host-supply : host I/O power supply (1.8V to 2.8V); +- vddio-cis-supply : CIS I/O power supply (1.2V to 1.8V); +- vdd-af-supply : lens power supply (2.8V); +- xshutdown-gpios : specifier of GPIO connected to the XSHUTDOWN pin; +- standby-gpios : specifier of GPIO connected to the STANDBY pin; +- clocks : contains the sensor's CIS_EXTCLK clock specifier; +- clock-names : contains "cis_extclk" entry; + +Optional properties: + +- clock-frequency : the frequency at which the "cis_extclk" clock should be + configured to operate, in Hz; if this property is not + specified default 24 MHz value will be used. + +The common video interfaces bindings (see video-interfaces.txt) should be used +to specify link from the S5C73M3 to an external image data receiver. The S5C73M3 +device node should contain one 'port' child node with an 'endpoint' subnode for +this purpose. The data link from a raw image sensor to the S5C73M3 can be +similarly specified, but it is optional since the S5C73M3 ISP and a raw image +sensor are usually inseparable and form a hybrid module. + +Following properties are valid for the endpoint node(s): + +endpoint subnode +---------------- + +- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in + video-interfaces.txt. This sensor doesn't support data lane remapping + and physical lane indexes in subsequent elements of the array should + be only consecutive ascending values. + +SPI device node +--------------- + +Required properties: + +- compatible : "samsung,s5c73m3"; + +For more details see description of the SPI busses bindings +(../spi/spi-bus.txt) and bindings of a specific bus controller. + +Example: + +i2c@138A000000 { + ... + s5c73m3@3c { + compatible = "samsung,s5c73m3"; + reg = <0x3c>; + vdd-int-supply = <&buck9_reg>; + vdda-supply = <&ldo17_reg>; + vdd-reg-supply = <&cam_io_reg>; + vddio-host-supply = <&ldo18_reg>; + vddio-cis-supply = <&ldo9_reg>; + vdd-af-supply = <&cam_af_reg>; + clock-frequency = <24000000>; + clocks = <&clk 0>; + clock-names = "cis_extclk"; + reset-gpios = <&gpf1 3 1>; + standby-gpios = <&gpm0 1 1>; + port { + s5c73m3_ep: endpoint { + remote-endpoint = <&csis0_ep>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +spi@1392000 { + ... + s5c73m3_spi: s5c73m3 { + compatible = "samsung,s5c73m3"; + reg = <0>; + ... + }; +}; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index b76ec0e..270d324 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -33,6 +35,7 @@ #include #include #include +#include #include "s5c73m3.h" @@ -46,6 +49,8 @@ static int update_fw; module_param(update_fw, int, 0644); #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K +#define S5C73M3_MIPI_DATA_LANES 4 +#define S5C73M3_CLK_NAME "cis_extclk" static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ @@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { ret = regulator_enable(state->supplies[i].consumer); if (ret) - goto err; + goto err_reg_dis; } + ret = clk_set_rate(state->clock, state->mclk_frequency); + if (ret < 0) + goto err_reg_dis; + + ret = clk_prepare_enable(state->clock); + if (ret < 0) + goto err_reg_dis; + + v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n", + clk_get_rate(state->clock)); + s5c73m3_gpio_deassert(state, STBY); usleep_range(100, 200); @@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) usleep_range(50, 100); return 0; -err: + +err_reg_dis: for (--i; i >= 0; i--) regulator_disable(state->supplies[i].consumer); return ret; @@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) if (s5c73m3_gpio_assert(state, STBY)) usleep_range(100, 200); + + clk_disable_unprepare(state->clock); + state->streaming = 0; state->isp_ready = 0; @@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) if (ret) goto err; } + return 0; err: for (++i; i < S5C73M3_MAX_SUPPLIES; i++) { @@ -1404,6 +1425,7 @@ static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); int ret = 0; + mutex_lock(&state->lock); if (on && !state->power) { @@ -1451,17 +1473,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - mutex_lock(&state->lock); - ret = __s5c73m3_power_on(state); - if (ret == 0) - s5c73m3_get_fw_version(state); - - __s5c73m3_power_off(state); - mutex_unlock(&state->lock); - - v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", - __func__, ret ? "failed" : "succeded", ret); - return ret; } @@ -1519,41 +1530,108 @@ static const struct v4l2_subdev_ops oif_subdev_ops = { .video = &s5c73m3_oif_video_ops, }; -static int s5c73m3_configure_gpios(struct s5c73m3 *state, - const struct s5c73m3_platform_data *pdata) +static int s5c73m3_configure_gpios(struct s5c73m3 *state) +{ + static const char * const gpio_names[] = { + "S5C73M3_STBY", "S5C73M3_RST" + }; + struct i2c_client *c = state->i2c_client; + struct s5c73m3_gpio *g = state->gpio; + int ret, i; + + for (i = 0; i < GPIO_NUM; ++i) { + unsigned int flags = GPIOF_DIR_OUT; + if (g[i].level) + flags |= GPIOF_INIT_HIGH; + ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, + gpio_names[i]); + if (ret) { + v4l2_err(c, "failed to request gpio %s\n", + gpio_names[i]); + return ret; + } + } + return 0; +} + +static int s5c73m3_parse_gpios(struct s5c73m3 *state) +{ + static const char * const prop_names[] = { + "standby-gpios", "xshutdown-gpios", + }; + struct device *dev = &state->i2c_client->dev; + struct device_node *node = dev->of_node; + int ret, i; + + for (i = 0; i < GPIO_NUM; ++i) { + enum of_gpio_flags of_flags; + + ret = of_get_named_gpio_flags(node, prop_names[i], + 0, &of_flags); + if (ret < 0) { + dev_err(dev, "failed to parse %s DT property\n", + prop_names[i]); + return -EINVAL; + } + state->gpio[i].gpio = ret; + state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW); + } + return 0; +} + +static int s5c73m3_get_platform_data(struct s5c73m3 *state) { struct device *dev = &state->i2c_client->dev; - const struct s5c73m3_gpio *gpio; - unsigned long flags; + const struct s5c73m3_platform_data *pdata = dev->platform_data; + struct device_node *node = dev->of_node; + struct device_node *node_ep; + struct v4l2_of_endpoint ep; int ret; - state->gpio[STBY].gpio = -EINVAL; - state->gpio[RST].gpio = -EINVAL; + if (!node) { + if (!pdata) { + dev_err(dev, "Platform data not specified\n"); + return -EINVAL; + } - gpio = &pdata->gpio_stby; - if (gpio_is_valid(gpio->gpio)) { - flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) - | GPIOF_EXPORT; - ret = devm_gpio_request_one(dev, gpio->gpio, flags, - "S5C73M3_STBY"); - if (ret < 0) - return ret; + state->mclk_frequency = pdata->mclk_frequency; + state->gpio[STBY] = pdata->gpio_stby; + state->gpio[RST] = pdata->gpio_reset; + return 0; + } - state->gpio[STBY] = *gpio; + if (of_property_read_u32(node, "clock-frequency", + &state->mclk_frequency)) { + state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ; + dev_info(dev, "using default %u Hz clock frequency\n", + state->mclk_frequency); } - gpio = &pdata->gpio_reset; - if (gpio_is_valid(gpio->gpio)) { - flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) - | GPIOF_EXPORT; - ret = devm_gpio_request_one(dev, gpio->gpio, flags, - "S5C73M3_RST"); - if (ret < 0) - return ret; + ret = s5c73m3_parse_gpios(state); + if (ret < 0) + return -EINVAL; - state->gpio[RST] = *gpio; + node_ep = v4l2_of_get_next_endpoint(node, NULL); + if (!node_ep) { + dev_warn(dev, "no endpoint defined for node: %s\n", + node->full_name); + return 0; } + v4l2_of_parse_endpoint(node_ep, &ep); + of_node_put(node_ep); + + if (ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(dev, "unsupported bus type\n"); + return -EINVAL; + } + /* + * Number of MIPI CSI-2 data lanes is currently not configurable, + * always a default value of 4 lanes is used. + */ + if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES) + dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n"); + return 0; } @@ -1561,21 +1639,24 @@ static int s5c73m3_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - const struct s5c73m3_platform_data *pdata = client->dev.platform_data; struct v4l2_subdev *sd; struct v4l2_subdev *oif_sd; struct s5c73m3 *state; int ret, i; - if (pdata == NULL) { - dev_err(&client->dev, "Platform data not specified\n"); - return -EINVAL; - } - state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; + state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); + if (IS_ERR(state->clock)) + return -EPROBE_DEFER; + + state->i2c_client = client; + ret = s5c73m3_get_platform_data(state); + if (ret < 0) + return ret; + mutex_init(&state->lock); sd = &state->sensor_sd; oif_sd = &state->oif_sd; @@ -1613,11 +1694,7 @@ static int s5c73m3_probe(struct i2c_client *client, if (ret < 0) return ret; - state->mclk_frequency = pdata->mclk_frequency; - state->bus_type = pdata->bus_type; - state->i2c_client = client; - - ret = s5c73m3_configure_gpios(state, pdata); + ret = s5c73m3_configure_gpios(state); if (ret) goto out_err; @@ -1651,9 +1728,29 @@ static int s5c73m3_probe(struct i2c_client *client, if (ret < 0) goto out_err; + oif_sd->dev = dev; + + ret = __s5c73m3_power_on(state); + if (ret < 0) + goto out_err1; + + ret = s5c73m3_get_fw_version(state); + __s5c73m3_power_off(state); + + if (ret < 0) { + dev_err(dev, "Device detection failed: %d\n", ret); + goto out_err1; + } + + ret = v4l2_async_register_subdev(oif_sd); + if (ret < 0) + goto out_err1; + v4l2_info(sd, "%s: completed succesfully\n", __func__); return 0; +out_err1: + s5c73m3_unregister_spi_driver(state); out_err: media_entity_cleanup(&sd->entity); return ret; @@ -1665,7 +1762,7 @@ static int s5c73m3_remove(struct i2c_client *client) struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); struct v4l2_subdev *sensor_sd = &state->sensor_sd; - v4l2_device_unregister_subdev(oif_sd); + v4l2_async_unregister_subdev(oif_sd); v4l2_ctrl_handler_free(oif_sd->ctrl_handler); media_entity_cleanup(&oif_sd->entity); @@ -1684,8 +1781,17 @@ static const struct i2c_device_id s5c73m3_id[] = { }; MODULE_DEVICE_TABLE(i2c, s5c73m3_id); +#ifdef CONFIG_OF +static const struct of_device_id s5c73m3_of_match[] = { + { .compatible = "samsung,s5c73m3" }, + { } +}; +MODULE_DEVICE_TABLE(of, s5c73m3_of_match); +#endif + static struct i2c_driver s5c73m3_i2c_driver = { .driver = { + .of_match_table = of_match_ptr(s5c73m3_of_match), .name = DRIVER_NAME, }, .probe = s5c73m3_probe, diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 8079e26..f60b265 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -27,6 +27,11 @@ #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" +static const struct of_device_id s5c73m3_spi_ids[] = { + { .compatible = "samsung,s5c73m3" }, + { } +}; + enum spi_direction { SPI_DIR_RX, SPI_DIR_TX @@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) spidrv->driver.name = S5C73M3_SPI_DRV_NAME; spidrv->driver.bus = &spi_bus_type; spidrv->driver.owner = THIS_MODULE; + spidrv->driver.of_match_table = s5c73m3_spi_ids; return spi_register_driver(spidrv); } diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h index 9d2c086..2917857 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3.h +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -17,6 +17,7 @@ #ifndef S5C73M3_H_ #define S5C73M3_H_ +#include #include #include #include @@ -321,6 +322,7 @@ enum s5c73m3_oif_pads { #define S5C73M3_MAX_SUPPLIES 6 +#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U struct s5c73m3_ctrls { struct v4l2_ctrl_handler handler; @@ -391,6 +393,8 @@ struct s5c73m3 { struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; struct s5c73m3_gpio gpio[GPIO_NUM]; + struct clk *clock; + /* External master clock frequency */ u32 mclk_frequency; /* Video bus type - MIPI-CSI2/paralell */