Received: by 2002:a6b:500f:0:0:0:0:0 with SMTP id e15csp22921iob; Tue, 3 May 2022 10:49:08 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwecVcjx/VFiplyVbWCeKUiMjiJRDbXiU5356d2M74jqBbOWOGiOt/GCWmGQFnpt7W2ASjD X-Received: by 2002:a62:5ec6:0:b0:50d:a467:3cb7 with SMTP id s189-20020a625ec6000000b0050da4673cb7mr16983410pfb.85.1651600147893; Tue, 03 May 2022 10:49:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1651600147; cv=none; d=google.com; s=arc-20160816; b=tHlgam3V5q2y3KvZuX4fZbcWB1NdXk4yKYAHErocURlgCMY9uHXGQumGVjFXa1nkuw J8RWxnGxWfiRebhyIQKvdjmWWfP2DX7+COB1aIjzcxsQyB7Tr0mg3+50qXsICNmW4EbW CRJhCzx/RX+nl0Ewar2vsSaHJSilC8oONXJ8zb6gzC3TfJVfr7hxHlCEsFmYy3jjuvM1 DuY6/9LItd0p8FZ/GR5ZPU4JVrjOXTgLLeYTFywUBNUOUHR8wyLpH7/lbuLacLVH5mvv 0mOqmzpO6+/zWXG4b3cVRK/Fk1zjsP3yBEcKH+1dAX/t1L5iLPHsvoZQu/53QSgA79Cd xZhw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:from; bh=TKp3G+hbGt6WvTCDcVTFc9cpiG+FLlldpyNhX3ufZNo=; b=g/CHqsBx4FidfI9euxcTEf14HsVhgQRciZBWXUtD6XptCB/BreJVULlTMMME/Pjw9f 4+lNRjS/cxyj06qDp0K04iQijghxtNqRFkL/8zvGm5YTH8zw75X4CxtpokicPyi+bjJC KbQcUThvX9j2vqjiKDtoVU+ukRcBfWxVljhNU5QuMqz63i5j2pEJO9hwmujt3pivNq1r Ah4kz3xBN85BNK3tlOGuzD1xGeDWfeypubYv7ZumesLSxy63RxMQ7LtGvdFLdRuGCr/U ZKZrsV+kkdnYG8PqI/p3JDK3diwArcXG8hMLRJo1hoph9oJUqRmhXFr0MQa69LnbjQar OtuA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id f4-20020a056a0022c400b0050d8efd790esi16474035pfj.205.2022.05.03.10.48.43; Tue, 03 May 2022 10:49:07 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238776AbiECPrJ (ORCPT + 99 others); Tue, 3 May 2022 11:47:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59290 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238734AbiECPrF (ORCPT ); Tue, 3 May 2022 11:47:05 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [IPv6:2001:4b98:dc4:8::230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 47EF42D1F7; Tue, 3 May 2022 08:43:31 -0700 (PDT) Received: (Authenticated sender: foss@0leil.net) by mail.gandi.net (Postfix) with ESMTPSA id 04C4124000D; Tue, 3 May 2022 15:43:27 +0000 (UTC) From: Quentin Schulz Cc: shawnx.tu@intel.com, mchehab@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Quentin Schulz , Quentin Schulz Subject: [PATCH 2/3] media: ov5675: add device-tree support Date: Tue, 3 May 2022 17:42:58 +0200 Message-Id: <20220503154259.1166203-2-foss+kernel@0leil.net> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220503154259.1166203-1-foss+kernel@0leil.net> References: <20220503154259.1166203-1-foss+kernel@0leil.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Quentin Schulz Until now, this driver only supported ACPI. This adds support for Device Tree too. This is heavily inspired by device tree support addition to OV8856 driver. The differentiation between ACPI and DT mode is done through an is_acpi_node check. Cc: Quentin Schulz Signed-off-by: Quentin Schulz --- drivers/media/i2c/ov5675.c | 134 +++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 82ba9f56baec..b885f0076a53 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -3,10 +3,13 @@ #include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -17,7 +20,7 @@ #define OV5675_LINK_FREQ_450MHZ 450000000ULL #define OV5675_SCLK 90000000LL -#define OV5675_MCLK 19200000 +#define OV5675_XVCLK_19_2 19200000 #define OV5675_DATA_LANES 2 #define OV5675_RGB_DEPTH 10 @@ -76,6 +79,14 @@ #define to_ov5675(_sd) container_of(_sd, struct ov5675, sd) +static const char * const ov5675_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5675_NUM_SUPPLIES ARRAY_SIZE(ov5675_supply_names) + enum { OV5675_LINK_FREQ_900MBPS, }; @@ -484,6 +495,9 @@ struct ov5675 { struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[OV5675_NUM_SUPPLIES]; /* V4L2 Controls */ struct v4l2_ctrl *link_freq; @@ -944,6 +958,52 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) return ret; } +static void __ov5675_power_off(struct ov5675 *ov5675) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + + if (is_acpi_node(dev_fwnode(&client->dev))) + return; + + gpiod_set_value_cansleep(ov5675->reset_gpio, 1); + usleep_range(1000, 1200); + + regulator_bulk_disable(OV5675_NUM_SUPPLIES, ov5675->supplies); + clk_disable_unprepare(ov5675->xvclk); +} + +static int __ov5675_power_on(struct ov5675 *ov5675) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + int ret; + + if (is_acpi_node(dev_fwnode(&client->dev))) + return 0; + + ret = clk_prepare_enable(ov5675->xvclk); + if (ret < 0) { + dev_err(&client->dev, "failed to enable xvclk: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(ov5675->reset_gpio, 1); + + /* Reset pulse should be at least 2ms */ + usleep_range(2000, 2200); + + ret = regulator_bulk_enable(OV5675_NUM_SUPPLIES, ov5675->supplies); + if (ret) { + clk_disable_unprepare(ov5675->xvclk); + return ret; + } + + gpiod_set_value_cansleep(ov5675->reset_gpio, 0); + + usleep_range(1000, 1200); + + return 0; +} + static int __maybe_unused ov5675_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -953,6 +1013,7 @@ static int __maybe_unused ov5675_suspend(struct device *dev) if (ov5675->streaming) ov5675_stop_streaming(ov5675); + __ov5675_power_off(ov5675); mutex_unlock(&ov5675->mutex); return 0; @@ -965,6 +1026,8 @@ static int __maybe_unused ov5675_resume(struct device *dev) int ret; mutex_lock(&ov5675->mutex); + + __ov5675_power_on(ov5675); if (ov5675->streaming) { ret = ov5675_start_streaming(ov5675); if (ret) { @@ -1106,32 +1169,60 @@ static const struct v4l2_subdev_internal_ops ov5675_internal_ops = { .open = ov5675_open, }; -static int ov5675_check_hwcfg(struct device *dev) +static int ov5675_get_hwcfg(struct ov5675 *ov5675, struct device *dev) { struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - u32 mclk; + u32 xvclk_rate; int ret; unsigned int i, j; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); if (ret) { dev_err(dev, "can't get clock frequency"); return ret; } - if (mclk != OV5675_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); + if (!is_acpi_node(fwnode)) { + ov5675->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov5675->xvclk)) { + ret = PTR_ERR(ov5675->xvclk); + dev_err(dev, "failed to get xvclk: %d\n", ret); + return ret; + } + + clk_set_rate(ov5675->xvclk, xvclk_rate); + xvclk_rate = clk_get_rate(ov5675->xvclk); + } + + if (xvclk_rate != OV5675_XVCLK_19_2) { + dev_err(dev, "external clock rate %u is unsupported", xvclk_rate); return -EINVAL; } + ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov5675->reset_gpio)) { + ret = PTR_ERR(ov5675->reset_gpio); + dev_err(dev, "failed to get reset-gpios: %d\n", ret); + return ret; + } + + for (i = 0; i < OV5675_NUM_SUPPLIES; i++) + ov5675->supplies[i].supply = ov5675_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES, + ov5675->supplies); + if (ret) + return ret; + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -1186,6 +1277,8 @@ static int ov5675_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); mutex_destroy(&ov5675->mutex); + __ov5675_power_off(ov5675); + return 0; } @@ -1195,25 +1288,31 @@ static int ov5675_probe(struct i2c_client *client) bool full_power; int ret; - ret = ov5675_check_hwcfg(&client->dev); + ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL); + if (!ov5675) + return -ENOMEM; + + ret = ov5675_get_hwcfg(ov5675, &client->dev); if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", + dev_err(&client->dev, "failed to get HW configuration: %d", ret); return ret; } - ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL); - if (!ov5675) - return -ENOMEM; - v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops); + ret = __ov5675_power_on(ov5675); + if (ret) { + dev_err(&client->dev, "failed to power on: %d\n", ret); + return ret; + } + full_power = acpi_dev_state_d0(&client->dev); if (full_power) { ret = ov5675_identify_module(ov5675); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); - return ret; + goto probe_power_off; } } @@ -1262,6 +1361,8 @@ static int ov5675_probe(struct i2c_client *client) probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov5675->sd.ctrl_handler); mutex_destroy(&ov5675->mutex); +probe_power_off: + __ov5675_power_off(ov5675); return ret; } @@ -1279,11 +1380,18 @@ static const struct acpi_device_id ov5675_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ov5675_acpi_ids); #endif +static const struct of_device_id ov5675_of_match[] = { + { .compatible = "ovti,ov5675", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov5675_of_match); + static struct i2c_driver ov5675_i2c_driver = { .driver = { .name = "ov5675", .pm = &ov5675_pm_ops, .acpi_match_table = ACPI_PTR(ov5675_acpi_ids), + .of_match_table = of_match_ptr(ov5675_of_match), }, .probe_new = ov5675_probe, .remove = ov5675_remove, -- 2.35.1