From patchwork Mon Feb 24 17:35:17 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylwester Nawrocki X-Patchwork-Id: 22457 X-Patchwork-Delegate: sylvester.nawrocki@gmail.com Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1WHzTQ-0008PL-SK; Mon, 24 Feb 2014 18:37:48 +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.72/mailfrontend-7) with esmtp id 1WHzTN-0001nD-3A; Mon, 24 Feb 2014 18:37:48 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752992AbaBXRhH (ORCPT + 1 other); Mon, 24 Feb 2014 12:37:07 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:26224 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752950AbaBXRhE (ORCPT ); Mon, 24 Feb 2014 12:37:04 -0500 Received: from epcpsbgm2.samsung.com (epcpsbgm2 [203.254.230.27]) by mailout1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0N1I00FGMGXRA750@mailout1.samsung.com>; Tue, 25 Feb 2014 02:37:03 +0900 (KST) X-AuditID: cbfee61b-b7f456d000006dfd-aa-530b833ef5c4 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm2.samsung.com (EPCPMTA) with SMTP id CF.CE.28157.E338B035; Tue, 25 Feb 2014 02:37:03 +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 <0N1I00672GVLX120@mmp1.samsung.com>; Tue, 25 Feb 2014 02:37:02 +0900 (KST) From: Sylwester Nawrocki To: linux-media@vger.kernel.org, devicetree@vger.kernel.org Cc: linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, robh+dt@kernel.org, mark.rutland@arm.com, galak@codeaurora.org, kyungmin.park@samsung.com, kgene.kim@samsung.com, a.hajda@samsung.com, Sylwester Nawrocki Subject: [PATCH v5 05/10] V4L: s5c73m3: Add device tree support Date: Mon, 24 Feb 2014 18:35:17 +0100 Message-id: <1393263322-28215-6-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1393263322-28215-1-git-send-email-s.nawrocki@samsung.com> References: <1393263322-28215-1-git-send-email-s.nawrocki@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrDLMWRmVeSWpSXmKPExsVy+t9jAV37Zu5gg+UHWCxurTvHajH/CJDo f7OQ1aJ3wVU2i7NNb9gtNj2+xmrRs2Erq8WM8/uYLJZev8hk0br3CLvF4TftrA7cHmvmrWH0 uNzXy+SxaVUnm8fmJfUefVtWMXp83iQXwBbFZZOSmpNZllqkb5fAldFz7QJLwZqEigWXf7E3 MM7162Lk5JAQMJFYtOw7C4QtJnHh3nq2LkYuDiGBRYwSXz4cYIdwOpgkes58BqtiEzCU6D3a xwhiiwhYSxz/cAysiFmglUmic8dNNpCEsIC9xL3fJ8CKWARUJbafv8oEYvMKuEncWvMJyOYA WqcgMWeSDUiYU8Bd4sK5zWCtQkAlv/+vZp3AyLuAkWEVo2hqQXJBcVJ6rpFecWJucWleul5y fu4mRnAYPpPewbiqweIQowAHoxIPb0cxd7AQa2JZcWXuIUYJDmYlEV6JfKAQb0piZVVqUX58 UWlOavEhRmkOFiVx3oOt1oFCAumJJanZqakFqUUwWSYOTqkGRq4e+TjLRSkSVcKNy7Tt7rvu bxLgy9xgeumXf/P2W9N6Xk72kPh5sfcB77LFP5cw+erLhHu/0l6TOmf5gxINRr8D1zV6q6KV z7m/SGXQqk8IXLOxQzNGXG+bzkeOSK75hYa1/5kWanq/4H6w6ujGSvddih+zr558ouDddGb7 ik+1U/ImvTvfpMRSnJFoqMVcVJwIAHqNsWY/AgAA 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: 2014.2.24.172717 X-PMX-Spam: Gauge=IIIIIIIII, Probability=9%, Report=' HTML_NO_HTTP 0.1, MULTIPLE_RCPTS 0.1, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, URI_ENDS_IN_HTML 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __CP_URI_IN_BODY 0, __HAS_FROM 0, __HAS_HTML 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_MAILTO 0, __URI_NS ' This patch adds the V4L2 asynchronous subdev registration and device tree support. Common clock API is used to control the sensor master clock from within the subdev. Signed-off-by: Andrzej Hajda Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park --- Changes since v4: - none. Changes since v3: - move clk_get from subdev s_power() to driver probe() callback and make it devm_clk_get(), - pass any errors from dev_clk_get() through, rather than overwriting with EPROBE_DEFER. Patch [1] is required for things to work correctly after this change. [1] http://www.spinics.net/lists/arm-kernel/msg306072.html --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 207 ++++++++++++++++++++++-------- drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 + drivers/media/i2c/s5c73m3/s5c73m3.h | 4 + 3 files changed, 167 insertions(+), 50 deletions(-) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index e7f555c..a445930 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++) { @@ -1396,6 +1417,8 @@ err: v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n", state->supplies[i].supply, r); } + + clk_prepare_enable(state->clock); return ret; } @@ -1451,17 +1474,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" : "succeeded", ret); - return ret; } @@ -1519,41 +1531,112 @@ 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->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); + if (IS_ERR(state->clock)) + return PTR_ERR(state->clock); - 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 +1644,20 @@ 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->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 +1695,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 +1729,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 successfully\n", __func__); return 0; +out_err1: + s5c73m3_unregister_spi_driver(state); out_err: media_entity_cleanup(&sd->entity); return ret; @@ -1665,7 +1763,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 +1782,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 9dfa516..9656b67 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/parallel */