From patchwork Sun Jan 21 15:14:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Akinobu Mita X-Patchwork-Id: 10176981 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2C11C601E7 for ; Sun, 21 Jan 2018 15:15:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1B3E0205F7 for ; Sun, 21 Jan 2018 15:15:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0E80B206AC; Sun, 21 Jan 2018 15:15:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3DA7E2018E for ; Sun, 21 Jan 2018 15:15:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751132AbeAUPPf (ORCPT ); Sun, 21 Jan 2018 10:15:35 -0500 Received: from mail-pg0-f67.google.com ([74.125.83.67]:40839 "EHLO mail-pg0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751124AbeAUPPe (ORCPT ); Sun, 21 Jan 2018 10:15:34 -0500 Received: by mail-pg0-f67.google.com with SMTP id g16so5089572pgn.7 for ; Sun, 21 Jan 2018 07:15:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=4FYPmKylDJd4x+8DpXzlJsZK8ccNRth+vETYn2sj/II=; b=RkvO+259B56rjiq1Bsky6SS7zysX5hnoWCFEIEKOyf7DocD6xzLoLNKlHwymsylikx UgLbQbOBdjdv98oZRg/NrGwf2Rny4sXTkajBK6EkRp0GlQqgDK0PhphMR2AIIjTac+B0 DqmIqriL5nnpyL0W+PxhH4HhT8xE4V8MWYq/zn5S2AxYa3cufGbFzDwdT/cBpfccg5K9 qbVasPQ7P7okbdorCFoBpmYKUCGj4u9ZNft31zbie6HzzpG0uvVDfQ5AWXm/E9Y+gJ03 Fv6ZutMV/PtG1utI2iiXLgmt/whu17edIQRk7tigEXLYvZnp39YTa2oeeeB4Q23MbTrj kPOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=4FYPmKylDJd4x+8DpXzlJsZK8ccNRth+vETYn2sj/II=; b=QqbpMsUnQWMlkQyBtO2jEH5AsMv7lB8hql+Sg1TgC3K28pnZiMBwHm8+sGR5R+CF1p tg9SU18OjOp3jvsits/107mVS29cZEo3xuPhZIx5qBVYXOvn6ZdyGt3FxQTVN/HmQ2JF hGKDrQmeBeLoVATtDJGrlqzFQZnfo5acY8PhJSOn3vPsxog8Z6UTF7WRVhoMK16guH0j K/wK7ORHXwcE+pryXGmg5CY5S+Yv9aIarAjAVAaY/UaV27xgA5ise9x/l4XW0Ne1WhYW y/fNg1s4ORwEMQmcwwaYvwRjKls/mcecz5dYg1jE5wDOzl7f0tClVlPzk46aO1UTa/kf Re1g== X-Gm-Message-State: AKwxytd3BCbag2x7HnXKAiAFyhSv9fENchcMGbTFvoVB2N9fVl5ZmY4N fobs5pjVoe+KSsKm1yvB9xs0jw== X-Google-Smtp-Source: AH8x227XO7mOOogugFbn1+gIsWajcH7DB+CUqbi3u7uqZQFBAKsa3K6pMM20o2KOyaXOSd0ecwEtvQ== X-Received: by 10.98.248.1 with SMTP id d1mr5515159pfh.222.1516547734240; Sun, 21 Jan 2018 07:15:34 -0800 (PST) Received: from localhost.localdomain ([240f:4:c2bc:1:d4b9:53ac:e3aa:e09f]) by smtp.gmail.com with ESMTPSA id r17sm25860832pfd.113.2018.01.21.07.15.31 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 21 Jan 2018 07:15:33 -0800 (PST) From: Akinobu Mita To: linux-media@vger.kernel.org Cc: Akinobu Mita , Sylwester Nawrocki , Jacopo Mondi , "H . Nikolaus Schaller" , Hugues Fruchet , Sakari Ailus , Mauro Carvalho Chehab , Rob Herring Subject: [PATCH v3 1/3] media: ov9650: support device tree probing Date: Mon, 22 Jan 2018 00:14:14 +0900 Message-Id: <1516547656-3879-2-git-send-email-akinobu.mita@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1516547656-3879-1-git-send-email-akinobu.mita@gmail.com> References: <1516547656-3879-1-git-send-email-akinobu.mita@gmail.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The ov9650 driver currently only supports legacy platform data probe. This change adds device tree probing. There has been an attempt to add device tree support for ov9650 driver by Hugues Fruchet as a part of the patchset that adds support of OV9655 camera (http://www.spinics.net/lists/linux-media/msg117903.html), but it wasn't merged into mainline because creating a separate driver for OV9655 is preferred. This is very similar to Hugues's patch, but not supporting new device. Cc: Sylwester Nawrocki Cc: Jacopo Mondi Cc: H. Nikolaus Schaller Cc: Hugues Fruchet Cc: Sakari Ailus Cc: Mauro Carvalho Chehab Cc: Rob Herring Reviewed-by: Sylwester Nawrocki Signed-off-by: Akinobu Mita --- * Changelog v3 - Add Reviewed-by: tag * Changelog v2 - Split binding documentation, suggested by Rob Herring and Jacopo Mondi - Remove ov965x_gpio_set() helper and open-code it, suggested by Jacopo Mondi and Sakari Ailus - Call clk_prepare_enable() in s_power callback instead of probe, suggested by Sakari Ailus - Unify clk and gpio configuration in a single if-else block and, also add a check either platform data or fwnode is actually specified, suggested by Jacopo Mondi - Add CONFIG_OF guards, suggested by Jacopo Mondi drivers/media/i2c/ov9650.c | 130 ++++++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 38 deletions(-) diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 69433e1..99a3eab 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -11,8 +11,10 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include +#include #include #include #include @@ -249,9 +251,10 @@ struct ov965x { struct v4l2_subdev sd; struct media_pad pad; enum v4l2_mbus_type bus_type; - int gpios[NUM_GPIOS]; + struct gpio_desc *gpios[NUM_GPIOS]; /* External master clock frequency */ unsigned long mclk_frequency; + struct clk *clk; /* Protects the struct fields below */ struct mutex lock; @@ -513,24 +516,27 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x) return 0; } -static void ov965x_gpio_set(int gpio, int val) -{ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, val); -} - -static void __ov965x_set_power(struct ov965x *ov965x, int on) +static int __ov965x_set_power(struct ov965x *ov965x, int on) { if (on) { - ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0); - ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0); + int ret = clk_prepare_enable(ov965x->clk); + + if (ret) + return ret; + + gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0); msleep(25); } else { - ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1); - ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1); + + clk_disable_unprepare(ov965x->clk); } ov965x->streaming = 0; + + return 0; } static int ov965x_s_power(struct v4l2_subdev *sd, int on) @@ -543,8 +549,8 @@ static int ov965x_s_power(struct v4l2_subdev *sd, int on) mutex_lock(&ov965x->lock); if (ov965x->power == !on) { - __ov965x_set_power(ov965x, on); - if (on) { + ret = __ov965x_set_power(ov965x, on); + if (!ret && on) { ret = ov965x_write_array(client, ov965x_init_regs); ov965x->apply_frame_fmt = 1; @@ -1408,16 +1414,17 @@ static const struct v4l2_subdev_ops ov965x_subdev_ops = { /* * Reset and power down GPIOs configuration */ -static int ov965x_configure_gpios(struct ov965x *ov965x, - const struct ov9650_platform_data *pdata) +static int ov965x_configure_gpios_pdata(struct ov965x *ov965x, + const struct ov9650_platform_data *pdata) { int ret, i; + int gpios[NUM_GPIOS]; - ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn; - ov965x->gpios[GPIO_RST] = pdata->gpio_reset; + gpios[GPIO_PWDN] = pdata->gpio_pwdn; + gpios[GPIO_RST] = pdata->gpio_reset; for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) { - int gpio = ov965x->gpios[i]; + int gpio = gpios[i]; if (!gpio_is_valid(gpio)) continue; @@ -1427,9 +1434,30 @@ static int ov965x_configure_gpios(struct ov965x *ov965x, return ret; v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio); - gpio_set_value(gpio, 1); + gpio_set_value_cansleep(gpio, 1); gpio_export(gpio, 0); - ov965x->gpios[i] = gpio; + ov965x->gpios[i] = gpio_to_desc(gpio); + } + + return 0; +} + +static int ov965x_configure_gpios(struct ov965x *ov965x) +{ + struct device *dev = &ov965x->client->dev; + + ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ov965x->gpios[GPIO_PWDN])) { + dev_info(dev, "can't get %s GPIO\n", "powerdown"); + return PTR_ERR(ov965x->gpios[GPIO_PWDN]); + } + + ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov965x->gpios[GPIO_RST])) { + dev_info(dev, "can't get %s GPIO\n", "reset"); + return PTR_ERR(ov965x->gpios[GPIO_RST]); } return 0; @@ -1443,7 +1471,10 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) int ret; mutex_lock(&ov965x->lock); - __ov965x_set_power(ov965x, 1); + ret = __ov965x_set_power(ov965x, 1); + if (ret) + goto out; + msleep(25); /* Check sensor revision */ @@ -1463,6 +1494,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) ret = -ENODEV; } } +out: mutex_unlock(&ov965x->lock); return ret; @@ -1476,23 +1508,39 @@ static int ov965x_probe(struct i2c_client *client, struct ov965x *ov965x; int ret; - if (!pdata) { - dev_err(&client->dev, "platform data not specified\n"); - return -EINVAL; - } - - if (pdata->mclk_frequency == 0) { - dev_err(&client->dev, "MCLK frequency not specified\n"); - return -EINVAL; - } - ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); if (!ov965x) return -ENOMEM; - mutex_init(&ov965x->lock); ov965x->client = client; - ov965x->mclk_frequency = pdata->mclk_frequency; + + if (pdata) { + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + ov965x->mclk_frequency = pdata->mclk_frequency; + + ret = ov965x_configure_gpios_pdata(ov965x, pdata); + if (ret < 0) + return ret; + } else if (dev_fwnode(&client->dev)) { + ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL); + if (IS_ERR(ov965x->clk)) + return PTR_ERR(ov965x->clk); + ov965x->mclk_frequency = clk_get_rate(ov965x->clk); + + ret = ov965x_configure_gpios(ov965x); + if (ret < 0) + return ret; + } else { + dev_err(&client->dev, + "Neither platform data nor device property specified\n"); + + return -EINVAL; + } + + mutex_init(&ov965x->lock); sd = &ov965x->sd; v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); @@ -1502,10 +1550,6 @@ static int ov965x_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - ret = ov965x_configure_gpios(ov965x, pdata); - if (ret < 0) - goto err_mutex; - ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad); @@ -1561,9 +1605,19 @@ static const struct i2c_device_id ov965x_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov965x_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov965x_of_match[] = { + { .compatible = "ovti,ov9650", }, + { .compatible = "ovti,ov9652", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov965x_of_match); +#endif + static struct i2c_driver ov965x_i2c_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ov965x_of_match), }, .probe = ov965x_probe, .remove = ov965x_remove,