From patchwork Sat Apr 20 01:18:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?WmhpIE1hbyAo5q+b5pm6KQ==?= X-Patchwork-Id: 13636887 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 76E6DC4345F for ; Sat, 20 Apr 2024 01:19:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=GG6pM5N8ZWWCy4sD3WNyuuHuHK1sBdm4R8BlznipUzE=; b=lembd/Bg4TQP+n 40q5MiQnuJIwtSqWLJUdvTU6041adR59IZ2YA7UmbK6sjTdpbIQmvYgzRnuea7ecDjb9bOYkHD6Fm wr9S36DHEd9qqi+tcrSeT+ALXvyVQd5Sol1R61WFCbcOkcDvmDHYVKz43dOlMcJwbJ4ayO5EuBNbg 4IojSBDj7AF7aKeb1JeDHUvQJKO0yGW6ZVKIpTcZYMyWgfDFpLfWbi2DArRTI5nOBDYhmE/k2kRPY ykvt2ICf/TuxNsRo3UuDIESHXJBbBIqFgsufDMEkVtpgAMbbs2qgJOoWswYQc5aF6m1Idj8ajHYcO qppKCXRE2EtR9atmTNMg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rxzO2-00000007UWb-1EvJ; Sat, 20 Apr 2024 01:19:26 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rxzNy-00000007UV3-3cSk; Sat, 20 Apr 2024 01:19:24 +0000 X-UUID: 04097b92feb411eeac1957ae9f99f617-20240419 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=CO4wjtqJ7941YvsG3EoEYP8MkcYX0u3FXi2wAP8WwL8=; b=PKkxjzoegg3t/jTwhy91q8W1NAUHbIDj9Wf/WL9Rq87VqAhQE53sVNa/EtsSbI8aRqPLJNDvQKD5ZnnybZIrQ75+xupSFn1OCTtNQFSbBRqujjDrrZW+FhI+nZ6lZbLWRS4rG5zUMLDzK7Lj1xNdwSdCmOVeC+j1kXgqkmivA3c=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.38,REQID:2f1307d2-4c6c-4c13-8753-e547b7c9aeef,IP:0,U RL:25,TC:0,Content:0,EDM:-30,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTI ON:release,TS:-5 X-CID-META: VersionHash:82c5f88,CLOUDID:74f2cb91-e2c0-40b0-a8fe-7c7e47299109,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:2,IP:nil,UR L:11|1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES: 1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_ULN,TF_CID_SPAM_SNR X-UUID: 04097b92feb411eeac1957ae9f99f617-20240419 Received: from mtkmbs14n2.mediatek.inc [(172.21.101.76)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1792101644; Fri, 19 Apr 2024 18:19:21 -0700 Received: from mtkmbs11n2.mediatek.inc (172.21.101.187) by mtkmbs11n1.mediatek.inc (172.21.101.185) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sat, 20 Apr 2024 09:19:18 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n2.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sat, 20 Apr 2024 09:19:16 +0800 From: Zhi Mao To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley CC: Matthias Brugger , AngeloGioacchino Del Regno , Zhi Mao , Philipp Zabel , Laurent Pinchart , Heiko Stuebner , Sakari Ailus , Hans Verkuil , Hans de Goede , Tomi Valkeinen , Alain Volmat , Paul Elder , Mehdi Djait , Andy Shevchenko , Bingbu Cao , , , , , , , , , <10572168@qq.com> Subject: [PATCH v1 1/2] media: dt-bindings: i2c: add Giantec GT97xx VCM Date: Sat, 20 Apr 2024 09:18:39 +0800 Message-ID: <20240420011840.23148-2-zhi.mao@mediatek.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240420011840.23148-1-zhi.mao@mediatek.com> References: <20240420011840.23148-1-zhi.mao@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240419_181922_938130_DA75C688 X-CRM114-Status: GOOD ( 13.68 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add YAML device tree binding for GT9768 & GT8769 VCM, and the relevant MAINTAINERS entries. Signed-off-by: Zhi Mao Reviewed-by: Krzysztof Kozlowski --- .../bindings/media/i2c/giantec,gt9769.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/giantec,gt9769.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/giantec,gt9769.yaml b/Documentation/devicetree/bindings/media/i2c/giantec,gt9769.yaml new file mode 100644 index 000000000000..6a9f49539b35 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/giantec,gt9769.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2020 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/giantec,gt9769.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Giantec Semiconductor, Crop. GT9768 & GT9769 Voice Coil Motor (VCM) + +maintainers: + - Zhi Mao + +description: |- + The Giantec GT9768 & GT9768 is a 10-bit DAC with current sink capability. + The DAC is controlled via I2C bus that operates at clock rates up to 1MHz. + This chip integrates Advanced Actuator Control (AAC) technology + and is intended for driving voice coil lens in camera modules. + +properties: + compatible: + enum: + - giantec,gt9768 + - giantec,gt9769 + + reg: + maxItems: 1 + + vin-supply: true + + vdd-supply: true + +required: + - compatible + - reg + - vin-supply + - vdd-supply + +additionalProperties: false + +examples: + - | + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera-lens@c { + compatible = "giantec,gt9769"; + reg = <0x0c>; + + vin-supply = <>97xx_vin>; + vdd-supply = <>97xx_vdd>; + }; + }; + +... From patchwork Sat Apr 20 01:18:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?WmhpIE1hbyAo5q+b5pm6KQ==?= X-Patchwork-Id: 13636889 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id EDE4AC4345F for ; Sat, 20 Apr 2024 01:21:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=Xk5PAoYlHJQdu3lUMggFut8wKJD3qyhBUGfml6b9Npw=; b=Kc2tvqgn4T6XGZ QkzqeS1Zrxo4Ms20krp+UAEXmyI9YtzHpjLP/IhbzHRqmqUHHjXaKKPX6GyZpMbxVRl4ZkkJ9Rhcl GB4+5WJ8jxa7IGnVmDctEdUABhoTXD36igHYQd5UyR6D9GOvD5/Ulx7Uw8sxzwY5U1Ktr0DQbmV0Y vkATIZaDOZjYVJvF6C0cVWXEK//3y+WeBhlf3Pf+P9V6JPYPxcYkV7FcZjleMv+sTuGM0nj5utRfe jX/fH4WbhUV5ih7dyAMorKwmYjXHuaINIIYTpju6g7XCuxc/ef4AhmMTuwBLM2OI/VFXFy6WlZ4dE Zcz72Xtn7xDeDPdU5Zew==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rxzQH-00000007Uyp-37Gs; Sat, 20 Apr 2024 01:21:45 +0000 Received: from casper.infradead.org ([2001:8b0:10b:1236::1]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rxzQF-00000007UyB-3z4v; Sat, 20 Apr 2024 01:21:44 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=Content-Type:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From:Sender :Reply-To:Content-ID:Content-Description; bh=uda2cqb2pFcyGcNrX4MtEfFo+dnCU7oaH84WCV1M28E=; b=CMiqsdL4QKh0LyIEmzbRz2VBaB 0JBQsb2zdR6iYvRvq4Wpd/BDyMSCEhBuktTQrpOaGj1bRYFqZqhg9lqtOENmGBRiWnukm5p39nGq2 hZZXAU08m7VlrCKIsCoV5Vgisb2RxZUPDypcpvmD8FX0cFtHsoKAYQpr1/Y0cOZoaAUoPMDoHE0I1 JepfDYu2lmNXASU9oto4Q3xUFtuX+2Hk6w0bli4wHNt309ndwRVyWXuY+Kr21dJe/q5TUFtiVUMZM 6QbPReQon0NiY+I6QTZuF/vBKLKeKchTGPfPdPyidwV7ni454BfqmmbwMExcxpu7huSqQmxcQTOM7 ORuhBu6A==; Received: from mailgw01.mediatek.com ([216.200.240.184]) by casper.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rxzQA-00000008xJy-3Qh5; Sat, 20 Apr 2024 01:21:42 +0000 X-UUID: 0e18f4aafeb411eeac1957ae9f99f617-20240419 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=uda2cqb2pFcyGcNrX4MtEfFo+dnCU7oaH84WCV1M28E=; b=tlJsrA3TzdCLQK/AlXD8dHU6uZOoQegWHwrYAuobovdlwekODV34lB36V0FXPZjkPw2SQhILpgkvtfOYg1K9O72McJBsu0RdbthMbYYlp0kiuzp1JTnvG0go52OLmNuwNAsWtheZzwC6fl7yQIDdssU9PbY5oP505KCeVqSH8LM=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.38,REQID:c11cb27e-e0bd-45a3-b4a9-56fd9e2abd0a,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:82c5f88,CLOUDID:5a8e21fb-ed05-4274-9204-014369d201e8,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1, SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-UUID: 0e18f4aafeb411eeac1957ae9f99f617-20240419 Received: from mtkmbs09n1.mediatek.inc [(172.21.101.35)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 15549468; Fri, 19 Apr 2024 18:19:37 -0700 Received: from mtkmbs11n2.mediatek.inc (172.21.101.187) by mtkmbs13n2.mediatek.inc (172.21.101.108) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sat, 20 Apr 2024 09:19:34 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n2.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sat, 20 Apr 2024 09:19:33 +0800 From: Zhi Mao To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley CC: Matthias Brugger , AngeloGioacchino Del Regno , Zhi Mao , Philipp Zabel , Laurent Pinchart , Heiko Stuebner , Sakari Ailus , Hans Verkuil , Hans de Goede , Tomi Valkeinen , Alain Volmat , Paul Elder , Mehdi Djait , Andy Shevchenko , Bingbu Cao , , , , , , , , , <10572168@qq.com> Subject: [PATCH v1 2/2] media: i2c: Add GT97xx VCM driver Date: Sat, 20 Apr 2024 09:18:40 +0800 Message-ID: <20240420011840.23148-3-zhi.mao@mediatek.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240420011840.23148-1-zhi.mao@mediatek.com> References: <20240420011840.23148-1-zhi.mao@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240420_022139_228465_14C54A94 X-CRM114-Status: GOOD ( 25.71 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add a V4L2 sub-device driver for Giantec GT97xx VCM. Signed-off-by: Zhi Mao --- drivers/media/i2c/Kconfig | 13 ++ drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gt97xx.c | 436 +++++++++++++++++++++++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 drivers/media/i2c/gt97xx.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 56f276b920ab..fcb330cebfe0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -759,6 +759,19 @@ config VIDEO_DW9807_VCM capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_GT97XX + tristate "GT97xx lens voice coil support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select V4L2_CCI_I2C + help + This is a driver for the GT97xx camera lens voice coil. + GT97xx is a 10 bit DAC with 100mA output current sink + capability. It is designed for linear control of + voice coil motors, controlled via I2C serial interface. + endmenu menu "Flash devices" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index dfbe6448b549..af36a7aa3d12 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_GC0308) += gc0308.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o +obj-$(CONFIG_VIDEO_GT97XX) += gt97xx.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o obj-$(CONFIG_VIDEO_HI847) += hi847.o diff --git a/drivers/media/i2c/gt97xx.c b/drivers/media/i2c/gt97xx.c new file mode 100644 index 000000000000..ccae190ffba6 --- /dev/null +++ b/drivers/media/i2c/gt97xx.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Giantec gt97xx VCM lens device + * + * Copyright 2024 MediaTek + * + * Zhi Mao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* gt97xx chip info register and name */ +#define GT97XX_IC_INFO_REG CCI_REG8(0x00) +#define GT9768_ID 0xE9 +#define GT9769_ID 0xE1 +#define GT97XX_NAME "gt97xx" + +/* + * Ring control and Power control register + * Bit[1] RING_EN + * 0: Direct mode + * 1: AAC mode (ringing control mode) + * Bit[0] PD + * 0: Normal operation mode + * 1: Power down mode + * requires waiting time after PD reset takes place. + */ +#define GT97XX_RING_PD_CONTROL_REG CCI_REG8(0x02) +#define GT97XX_PD_MODE_OFF 0x00 +#define GT97XX_PD_MODE_EN BIT(0) +#define GT97XX_AAC_MODE_EN BIT(1) + +/* + * DAC is a 10bit address to control the VCM position. + * DAC_MSB: D[9:8] (ADD: 0x03) + * DAC_LSB: D[7:0] (ADD: 0x04) + */ +#define GT97XX_DAC_ADDR_REG CCI_REG16(0x03) + +#define GT97XX_MOVE_STEPS 16 +#define GT97XX_MAX_FOCUS_POS (BIT(10) - 1) + +#define GT97XX_SLEEP_US (1 * USEC_PER_MSEC) + +enum vcm_giantec_reg_desc { + GT_IC_INFO_REG, + GT_RING_PD_CONTROL_REG, + GT_DAC_ADDR_REG, + GT_MAX_REG +}; + +struct vcm_giantec_of_data { + unsigned int id; + unsigned int regs[GT_MAX_REG]; +}; + +static const char *const gt97xx_supply_names[] = { + "vin", + "vdd", +}; + +/* gt97xx device structure */ +struct gt97xx { + struct v4l2_subdev sd; + + struct regulator_bulk_data supplies[ARRAY_SIZE(gt97xx_supply_names)]; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *focus; + + struct regmap *regmap; + + const struct vcm_giantec_of_data *chip; +}; + +static inline struct gt97xx *sd_to_gt97xx(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct gt97xx, sd); +} + +struct regval_list { + u8 reg_num; + u8 value; +}; + +static int gt97xx_set_dac(struct gt97xx *gt97xx, u16 val) +{ + /* Write VCM position to registers */ + return cci_write(gt97xx->regmap, + gt97xx->chip->regs[GT_DAC_ADDR_REG], val, NULL); +} + +static int gt97xx_identify_module(struct gt97xx *gt97xx) +{ + int ret; + u64 ic_id; + struct i2c_client *client = v4l2_get_subdevdata(>97xx->sd); + + ret = cci_read(gt97xx->regmap, gt97xx->chip->regs[GT_IC_INFO_REG], + &ic_id, NULL); + if (ret < 0) + return ret; + + if (ic_id != gt97xx->chip->id) { + dev_err(&client->dev, "chip id mismatch: 0x%x!=0x%llx", + gt97xx->chip->id, ic_id); + return -1; + } + + return 0; +} + +static int gt97xx_init(struct gt97xx *gt97xx) +{ + int ret, val; + + ret = gt97xx_identify_module(gt97xx); + if (ret < 0) + return ret; + + /* Reset PD_CONTROL */ + ret = cci_write(gt97xx->regmap, + gt97xx->chip->regs[GT_RING_PD_CONTROL_REG], + GT97XX_PD_MODE_OFF, NULL); + if (ret < 0) + return ret; + + /* Need waiting delay time after PD reset */ + fsleep(GT97XX_SLEEP_US); + + /* Enable ACC mode */ + ret = cci_write(gt97xx->regmap, + gt97xx->chip->regs[GT_RING_PD_CONTROL_REG], + GT97XX_AAC_MODE_EN, NULL); + if (ret < 0) + return ret; + + for (val = gt97xx->focus->val % GT97XX_MOVE_STEPS; + val <= gt97xx->focus->val; val += GT97XX_MOVE_STEPS) { + ret = gt97xx_set_dac(gt97xx, val); + if (ret) + return ret; + + fsleep(GT97XX_SLEEP_US); + } + + return 0; +} + +static int gt97xx_release(struct gt97xx *gt97xx) +{ + int ret, val; + + val = round_down(gt97xx->focus->val, GT97XX_MOVE_STEPS); + for (; val >= 0; val -= GT97XX_MOVE_STEPS) { + ret = gt97xx_set_dac(gt97xx, val); + if (ret) + return ret; + + fsleep(GT97XX_SLEEP_US); + } + + return 0; +} + +static int gt97xx_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gt97xx *gt97xx = sd_to_gt97xx(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(gt97xx_supply_names), + gt97xx->supplies); + if (ret < 0) + dev_err(dev, "failed to enable regulators\n"); + + return ret; +} + +static int gt97xx_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gt97xx *gt97xx = sd_to_gt97xx(sd); + int ret; + + ret = regulator_bulk_disable(ARRAY_SIZE(gt97xx_supply_names), + gt97xx->supplies); + if (ret < 0) + dev_err(dev, "failed to disable regulators\n"); + + return ret; +} + +static int gt97xx_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gt97xx *gt97xx = sd_to_gt97xx(sd); + + gt97xx_release(gt97xx); + gt97xx_power_off(dev); + + return 0; +} + +static int gt97xx_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gt97xx *gt97xx = sd_to_gt97xx(sd); + int ret; + + ret = gt97xx_power_on(dev); + if (ret < 0) { + dev_err(dev, "failed to power_on\n"); + return ret; + } + + /* Need waited before sending I2C commands after power-up */ + fsleep(GT97XX_SLEEP_US); + + ret = gt97xx_init(gt97xx); + if (ret < 0) + goto disable_power; + + return 0; + +disable_power: + gt97xx_power_off(dev); + + return ret; +} + +static int gt97xx_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gt97xx *gt97xx = + container_of(ctrl->handler, struct gt97xx, ctrls); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return gt97xx_set_dac(gt97xx, ctrl->val); + + return 0; +} + +static const struct v4l2_ctrl_ops gt97xx_ctrl_ops = { + .s_ctrl = gt97xx_set_ctrl, +}; + +static int gt97xx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return pm_runtime_resume_and_get(sd->dev); +} + +static int gt97xx_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return pm_runtime_put(sd->dev); +} + +static const struct v4l2_subdev_internal_ops gt97xx_int_ops = { + .open = gt97xx_open, + .close = gt97xx_close, +}; + +static const struct v4l2_subdev_core_ops gt97xx_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops gt97xx_ops = { + .core = >97xx_core_ops, +}; + +static int gt97xx_init_controls(struct gt97xx *gt97xx) +{ + struct v4l2_ctrl_handler *hdl = >97xx->ctrls; + const struct v4l2_ctrl_ops *ops = >97xx_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + gt97xx->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 0, + GT97XX_MAX_FOCUS_POS, + 1, 0); + + if (hdl->error) + return hdl->error; + + gt97xx->sd.ctrl_handler = hdl; + + return 0; +} + +static int gt97xx_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gt97xx *gt97xx; + unsigned int i; + int ret; + + gt97xx = devm_kzalloc(dev, sizeof(*gt97xx), GFP_KERNEL); + if (!gt97xx) + return -ENOMEM; + + gt97xx->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(gt97xx->regmap)) + return dev_err_probe(dev, PTR_ERR(gt97xx->regmap), + "failed to init CCI\n"); + + /* Initialize subdev */ + v4l2_i2c_subdev_init(>97xx->sd, client, >97xx_ops); + + gt97xx->chip = device_get_match_data(dev); + + for (i = 0; i < ARRAY_SIZE(gt97xx_supply_names); i++) + gt97xx->supplies[i].supply = gt97xx_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(gt97xx_supply_names), + gt97xx->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + /* Initialize controls */ + ret = gt97xx_init_controls(gt97xx); + if (ret) + goto err_free_handler; + + /* Initialize subdev */ + gt97xx->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + gt97xx->sd.internal_ops = >97xx_int_ops; + gt97xx->sd.entity.function = MEDIA_ENT_F_LENS; + + ret = media_entity_pads_init(>97xx->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_handler; + + /* Power on and initialize hardware */ + ret = gt97xx_runtime_resume(dev); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to power on\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_idle(dev); + + ret = v4l2_async_register_subdev(>97xx->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to register V4L2 subdev\n"); + goto err_power_off; + } + + return 0; + +err_power_off: + pm_runtime_disable(dev); +err_clean_entity: + media_entity_cleanup(>97xx->sd.entity); +err_free_handler: + v4l2_ctrl_handler_free(>97xx->ctrls); + + return ret; +} + +static void gt97xx_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gt97xx *gt97xx = sd_to_gt97xx(sd); + + v4l2_async_unregister_subdev(>97xx->sd); + v4l2_ctrl_handler_free(>97xx->ctrls); + media_entity_cleanup(>97xx->sd.entity); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gt97xx_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(gt97xx_pm_ops, + gt97xx_runtime_suspend, + gt97xx_runtime_resume, + NULL); + +static const struct vcm_giantec_of_data gt9768_data = { + .id = GT9768_ID, + .regs[GT_IC_INFO_REG] = GT97XX_IC_INFO_REG, + .regs[GT_RING_PD_CONTROL_REG] = GT97XX_RING_PD_CONTROL_REG, + .regs[GT_DAC_ADDR_REG] = GT97XX_DAC_ADDR_REG, +}; + +static const struct vcm_giantec_of_data gt9769_data = { + .id = GT9769_ID, + .regs[GT_IC_INFO_REG] = GT97XX_IC_INFO_REG, + .regs[GT_RING_PD_CONTROL_REG] = GT97XX_RING_PD_CONTROL_REG, + .regs[GT_DAC_ADDR_REG] = GT97XX_DAC_ADDR_REG, +}; + +static const struct of_device_id gt97xx_of_table[] = { + { .compatible = "giantec,gt9768", .data = >9768_data }, + { .compatible = "giantec,gt9769", .data = >9769_data }, + {} +}; +MODULE_DEVICE_TABLE(of, gt97xx_of_table); + +static struct i2c_driver gt97xx_i2c_driver = { + .driver = { + .name = GT97XX_NAME, + .pm = pm_ptr(>97xx_pm_ops), + .of_match_table = gt97xx_of_table, + }, + .probe = gt97xx_probe, + .remove = gt97xx_remove, +}; +module_i2c_driver(gt97xx_i2c_driver); + +MODULE_AUTHOR("Zhi Mao "); +MODULE_DESCRIPTION("GT97xx VCM driver"); +MODULE_LICENSE("GPL");