From nobody Sun Feb 8 07:26:17 2026 Received: from NAM12-DM6-obe.outbound.protection.outlook.com (mail-dm6nam12on2070.outbound.protection.outlook.com [40.107.243.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 24E9319258E; Mon, 9 Jun 2025 19:44:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.243.70 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749498273; cv=fail; b=c6ET88IwI0KOc9kV4RZpQW8i/nKv/FqaRCsL95mA7icuo6dHiUNk2QHmxn25EGrDLdzI0mWQS40yOUrmns/j/5KMqSmkLzw7pFLKi8G+ndkntGnOLClxgDT1gt8ayk39oqDCepK49gQ+kCBM28HdreQbd0zTUU5QeJRIKktkhq4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749498273; c=relaxed/simple; bh=cV+YQBIIT0CmlAyP6DOA32rD/jNql7Om6wTMuYyOuTA=; h=From:To:CC:Subject:Date:Message-ID:MIME-Version:Content-Type; b=ljDrfjuQgzhDkos67sFyOodZmOG+HvgsjkpIjjQd2laXGvpuhdeKq1EZWY9q/sfsQkehn5gfgcLScyLfUm/EEFDSMtfYdTE/HAiazrnPWpii2gwzhh4YZoGZqoH8DhboK5aPXZ0kDNEtxXvCtBNUc75MjmpcMLbcjZf+qgwoZXk= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=cI2tmsqR; arc=fail smtp.client-ip=40.107.243.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="cI2tmsqR" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Uo/5mjQF6HM/ZgU9MabB8hv6MxEC2zcRgDoZkK5luMs5tAJcugOxKNEqsvmuuf49pslXbEHWg99Waovv3ITJ5M3pR/D14kgcYGNiLA2G0zll2frGwZ7RapYwndPhjH+0RteNlvTovwvnAbS7NmGG3JSQeoqzbXqYf22cvoGpidlBDoYpDF2dp/uzGQKFcd77KnSt+qiFWJWQDvJV/NYokIWR/OiHeA5rL3ygFR8yebTFIu8KooWpoobZycJw5o2VmBTyNuDcMwZ6tIHJbiKURX6Ng+Kh7ZmCLDym+O+mogIcyNTnbK+HahLnFX6HuQCNJFrXhDo34LOoDHRPGHGJKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=8V26KJXPxp0S95jKxKySktrj3Bw78fPOeibiUR1RC4g=; b=iv54cHs5jP3zQlAlx1fD18R83lYz9yQukYmMvxnJYkPfkYCPx1fkPY5GU6Jgs39PrwryDceRvKeQB9KFQTEXKn3ZnrUTJxD86KaBtF8CaH5XloJ+QZldIoZnd5D/+y9ktnpn1I4EgYsdB4p9umbWRzgOff8K9kOoy/XLI6k5vnHe+CxB+xi+D6FqzFtiSykdwo7olHhv7Xz6rMA1BTu1Zin3jyqKk0hBBKDRyalxxveGF0haZMxf78PrjoXbGTD4KepYEPw5ymmV27eS+wgCaMl1Mz+QCzE9rtWJyBbZKIh3PIR/Yh9RWoGuiiPfPo8Lz4of4MKJ/ov5h8HYzJ21bA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=8V26KJXPxp0S95jKxKySktrj3Bw78fPOeibiUR1RC4g=; b=cI2tmsqRxQ6hiXqeekrwfQ6o7Q74kgppYGnxcR/Skk3GG/7dHIbe6i4xS2w788LBKz8TGQJwgQXTbHFGYvcJFmc5If/0VfERJbXyEAflCXZZy86ozV3x3PhbeN3dl0ja6rt957u5jcRZPmHggzw2NWal4JczRZ6adHMR1wCUEak= Received: from BY3PR05CA0028.namprd05.prod.outlook.com (2603:10b6:a03:254::33) by SJ0PR12MB7473.namprd12.prod.outlook.com (2603:10b6:a03:48d::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8792.37; Mon, 9 Jun 2025 19:44:28 +0000 Received: from SJ1PEPF00002319.namprd03.prod.outlook.com (2603:10b6:a03:254:cafe::6) by BY3PR05CA0028.outlook.office365.com (2603:10b6:a03:254::33) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8769.18 via Frontend Transport; Mon, 9 Jun 2025 19:44:28 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by SJ1PEPF00002319.mail.protection.outlook.com (10.167.242.229) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.8835.15 via Frontend Transport; Mon, 9 Jun 2025 19:44:28 +0000 Received: from maple-stxh-linux-10.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Mon, 9 Jun 2025 14:44:26 -0500 From: Pratap Nirujogi To: , , , , , , , , , CC: , , , , , , , , Pratap Nirujogi Subject: [PATCH v3 RESEND] media: i2c: Add OV05C10 camera sensor driver Date: Mon, 9 Jun 2025 15:42:22 -0400 Message-ID: <20250609194321.1611419-1-pratap.nirujogi@amd.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SATLEXMB03.amd.com (10.181.40.144) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ1PEPF00002319:EE_|SJ0PR12MB7473:EE_ X-MS-Office365-Filtering-Correlation-Id: 4c95a2a9-d09b-4808-21b9-08dda78e0be2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|36860700013|82310400026|1800799024|376014|7416014|921020; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?KexKiHej1hw0fg9lNPKQLQQyHid+X5Ug8SxNJyRIJSBbUDy1XhKzOhQV8JYk?= =?us-ascii?Q?3dea4gTvzthqwMLsmp5UF6+IX0Vk1mfttrnkduAlkuPKRSk6Y/WAgeIYozMO?= =?us-ascii?Q?D/kKgh9vzjBxFNQ2OCuwewxlA7EvzfS/9SbghG6jPAJaz+Nv9sWqrt47nKcp?= =?us-ascii?Q?RkhcLvjyXMxTniTxeKlLPAhegsL8skEfbhJezhcQc79tc/l091pE2mYg3TM6?= =?us-ascii?Q?9xh2EIHrDdKsUIYjxyaZVZtLJvOTmiwYrVD8LA78aoGKfHBO+yRBhvBW7uBu?= =?us-ascii?Q?SzhkWnA01Gc7fe27nC+jcchUkSCbPql2LsdyoWKLDhQyNZhXbY3+y6XDYsqR?= =?us-ascii?Q?oXJHpng41XcTi+V6nQS2+yIsHFnO5LZ3O2sxQOM2ZL2Qgf8aOQVsSiKFLVtM?= =?us-ascii?Q?6TH769yS0B85i4w6SNzP/AJLp9tlFCUoBLVQzc8Em1/iB0Heigpw12vj+Yvv?= =?us-ascii?Q?JU8Eeg7UyFU1IUDShJMhUIKrXLHgsBkNvrpX01v0yixRv29SJKYycdAEdTp7?= =?us-ascii?Q?fqH9thkCwdYOWTvXTBFE+UnAlHinjy3fFS+If9U2RNI8+ztxHx34GA0KnN+Z?= =?us-ascii?Q?kcJlNz8IIT71EQqlbi39xAKahQq/b9gjXFnrJrv0zJQ0resMOgjrP5Tr2UFN?= =?us-ascii?Q?0CVNY/RDhy0vHSTnszypctoLtOvU0EgDIk0jyV/h3zSODOXKcwxvUSwBOB3h?= =?us-ascii?Q?RK3Dq0Sx5JLxHuGhDPekO7C8FVAzjeTwQVY9gmny58mHuz62IxTRJeWbtVSM?= =?us-ascii?Q?RvcUePLsbD1TLTM7fw2JbwOBvKiRNB8c5j2oYdvZtn3Nv7WQ1UQDUQcquoYD?= =?us-ascii?Q?IpskTv/ogyvlLVdmHgT2clmv7J5ZWO8MpgMvJHim2obkXJcZSZ2vcPajAPBX?= =?us-ascii?Q?4ADW50nJzeQvHwpfmqpcjapyfZve0EkkZLF/ApkbcOyiuL/86yKuQnUNm2Q6?= =?us-ascii?Q?0fFShG3gnlhsJHPffcruuQqiGW5gqQSWYiufMX9atHr8uCaUCEb12ohQpkMT?= =?us-ascii?Q?tAhUTEkwQ7EzZRUH8kQvsQkyDeG19yTvvUhAKKiyKUwZFHX0uHMLHKqemOUC?= =?us-ascii?Q?JL2Dpuf1VU2Orvu4DP3sPAu8kgnRB2dKlVu7oAUAq90IcObrh87Ws9lLak5Y?= =?us-ascii?Q?BOQhV4PTPLhA6LDDKn+uA+PL5o6YylKToVWUj6A9u2RoTZ+VPbiMsKfCDLcb?= =?us-ascii?Q?oDROGEHUeb3G8TJ9AvUrWVbz88olPd7eA+C3ALKMx37zfyPvJo3J41Xahgpx?= =?us-ascii?Q?UrkuV5E44jZnoc49IkFkGoGAep59/Wyyqu09ULagotiAbv8P2ReWnnBocI71?= =?us-ascii?Q?o9wp/ircIEMwDKMlVzmYUD4HgRAj7xEXIQ/7+lzzt4jEw08wQ+W2+yXIkNxe?= =?us-ascii?Q?CgBgn8chStVo3rDSTWJ3fz5B+r+lnCiyeoYmlz1aDnmVvooYgSuOZB2r4g06?= =?us-ascii?Q?1eUn+2HPxutlT0yxGM1cyZGLQNhbBb7C5LNn92HKurN/OjZTEE4+jxTIqRMx?= =?us-ascii?Q?YVFZjEYSh7s/RF9ViVqngzrP8PK2Uga+EkvcWrACCi6WRh3Vaq363sInbQ?= =?us-ascii?Q?=3D=3D?= X-Forefront-Antispam-Report: CIP:165.204.84.17;CTRY:US;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:SATLEXMB04.amd.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230040)(36860700013)(82310400026)(1800799024)(376014)(7416014)(921020);DIR:OUT;SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 Jun 2025 19:44:28.1775 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 4c95a2a9-d09b-4808-21b9-08dda78e0be2 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.17];Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: SJ1PEPF00002319.namprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR12MB7473 Content-Type: text/plain; charset="utf-8" Add driver for OmniVision 5.2M OV05C10 sensor. This driver supports only the full size normal 2888x1808@30fps 2-lane sensor profile. Co-developed-by: Venkata Narendra Kumar Gutta Signed-off-by: Venkata Narendra Kumar Gutta Co-developed-by: Bin Du Signed-off-by: Bin Du Signed-off-by: Pratap Nirujogi --- Changes v2 -> v3: * Update "refclk" property variable as "clock-frequency". * Update sensor GPIO connector id name. * Fix sensor v4l2 compliance issue. * Fix license info. * Address review comments. MAINTAINERS | 8 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov05c10.c | 1061 +++++++++++++++++++++++++++++++++++ 4 files changed, 1080 insertions(+) create mode 100644 drivers/media/i2c/ov05c10.c diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa16..caca25d00bf2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18303,6 +18303,14 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml F: drivers/media/i2c/ov02e10.c =20 +OMNIVISION OV05C10 SENSOR DRIVER +M: Nirujogi Pratap +M: Bin Du +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/i2c/ov05c10.c + OMNIVISION OV08D10 SENSOR DRIVER M: Jimmy Su L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e68202954a8f..1662fb29d75c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -377,6 +377,16 @@ config VIDEO_OV02C10 To compile this driver as a module, choose M here: the module will be called ov02c10. =20 +config VIDEO_OV05C10 + tristate "OmniVision OV05C10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV05C10 camera. + + To compile this driver as a module, choose M here: the + module will be called OV05C10. + config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5873d29433ee..b4a1d721a7f2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_VIDEO_OV01A10) +=3D ov01a10.o obj-$(CONFIG_VIDEO_OV02A10) +=3D ov02a10.o obj-$(CONFIG_VIDEO_OV02C10) +=3D ov02c10.o obj-$(CONFIG_VIDEO_OV02E10) +=3D ov02e10.o +obj-$(CONFIG_VIDEO_OV05C10) +=3D ov05c10.o obj-$(CONFIG_VIDEO_OV08D10) +=3D ov08d10.o obj-$(CONFIG_VIDEO_OV08X40) +=3D ov08x40.o obj-$(CONFIG_VIDEO_OV13858) +=3D ov13858.o diff --git a/drivers/media/i2c/ov05c10.c b/drivers/media/i2c/ov05c10.c new file mode 100644 index 000000000000..9a1e493c4073 --- /dev/null +++ b/drivers/media/i2c/ov05c10.c @@ -0,0 +1,1061 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (C) 2025 Advanced Micro Devices, Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "ov05c10" +#define OV05C10_REF_CLK (24 * HZ_PER_MHZ) + +#define MODE_WIDTH 2888 +#define MODE_HEIGHT 1808 + +#define PAGE_NUM_MASK 0xff000000 +#define PAGE_NUM_SHIFT 24 +#define REG_ADDR_MASK 0x00ffffff + +#define OV05C10_SYSCTL_PAGE (0 << PAGE_NUM_SHIFT) +#define OV05C10_CISCTL_PAGE (1 << PAGE_NUM_SHIFT) +#define OV05C10_ISPCTL_PAGE (4 << PAGE_NUM_SHIFT) + +/* Chip ID */ +#define OV05C10_REG_CHIP_ID (CCI_REG24(0x00) | OV05C10_SYSCTL_PAGE) +#define OV05C10_CHIP_ID 0x43055610 + +/* Control registers */ +#define OV05C10_REG_TRIGGER (CCI_REG8(0x01) | OV05C10_CISCTL_PAGE) +#define OV05C_REG_TRIGGER_START BIT(0) + +/* Exposure control */ +#define OV05C10_REG_EXPOSURE (CCI_REG24(0x02) | OV05C10_CISCTL_PAGE) +#define OV05C10_EXPOSURE_MAX_MARGIN 33 +#define OV05C10_EXPOSURE_MIN 4 +#define OV05C10_EXPOSURE_STEP 1 +#define OV05C10_EXPOSURE_DEFAULT 0x40 + +/* V_TIMING internal */ +#define OV05C10_REG_VTS (CCI_REG16(0x05) | OV05C10_CISCTL_PAGE) +#define OV05C10_VTS_30FPS 1860 +#define OV05C10_VTS_MAX 0x7fff + +/* Test Pattern Control */ +#define OV05C10_REG_TEST_PATTERN (CCI_REG8(0x12) | OV05C10_ISPCTL_PAGE) +#define OV05C10_TEST_PATTERN_ENABLE BIT(0) +#define OV05C10_REG_TEST_PATTERN_CTL (CCI_REG8(0xf3) | OV05C10_ISPCTL_PAGE) +#define OV05C10_REG_TEST_PATTERN_XXX BIT(0) + +/* Digital gain control */ +#define OV05C10_REG_DGTL_GAIN_H (CCI_REG8(0x21) | OV05C10_CISCTL_PAGE) +#define OV05C10_REG_DGTL_GAIN_L (CCI_REG8(0x22) | OV05C10_CISCTL_PAGE) + +#define OV05C10_DGTL_GAIN_MIN 0x40 +#define OV05C10_DGTL_GAIN_MAX 0xff +#define OV05C10_DGTL_GAIN_DEFAULT 0x40 +#define OV05C10_DGTL_GAIN_STEP 1 + +#define OV05C10_DGTL_GAIN_L_MASK 0xff +#define OV05C10_DGTL_GAIN_H_SHIFT 8 +#define OV05C10_DGTL_GAIN_H_MASK 0xff00 + +/* Analog gain control */ +#define OV05C10_REG_ANALOG_GAIN (CCI_REG8(0x24) | OV05C10_CISCTL_PAGE) +#define OV05C10_ANA_GAIN_MIN 0x80 +#define OV05C10_ANA_GAIN_MAX 0x07c0 +#define OV05C10_ANA_GAIN_STEP 1 +#define OV05C10_ANA_GAIN_DEFAULT 0x80 + +/* H TIMING internal */ +#define OV05C10_REG_HTS (CCI_REG16(0x37) | OV05C10_CISCTL_PAGE) +#define OV05C10_HTS_30FPS 0x0280 + +/* Page selection */ +#define OV05C10_REG_PAGE_CTL CCI_REG8(0xfd) + +#define NUM_OF_PADS 1 + +#define OV05C10_GET_PAGE_NUM(reg) (((reg) & PAGE_NUM_MASK) >>\ + PAGE_NUM_SHIFT) +#define OV05C10_GET_REG_ADDR(reg) ((reg) & REG_ADDR_MASK) + +enum { + OV05C10_LINK_FREQ_900MHZ_INDEX, +}; + +struct ov05c10_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +/* Mode : resolution and related config&values */ +struct ov05c10_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + /* number of lanes */ + u32 lanes; + + /* V-timing */ + u32 vts_def; + u32 vts_min; + + /* HTS */ + u32 hts; + + /* Index of Link frequency config to be used */ + u32 link_freq_index; + + /* Default register values */ + struct ov05c10_reg_list reg_list; +}; + +static const s64 ov05c10_link_frequencies[] =3D { + 925 * HZ_PER_MHZ, +}; + +/* 2888x1808 30fps, 1800mbps, 2lane, 24mhz */ +static const struct cci_reg_sequence ov05c10_2888x1808_regs[] =3D { + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x00 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x0b }, + { CCI_REG8(0xc1), 0x09 }, + { CCI_REG8(0x21), 0x06 }, + { CCI_REG8(0x14), 0x78 }, + { CCI_REG8(0xe7), 0x03 }, + { CCI_REG8(0xe7), 0x00 }, + { CCI_REG8(0x21), 0x00 }, + { CCI_REG8(0xfd), 0x01 }, + { CCI_REG8(0x03), 0x00 }, + { CCI_REG8(0x04), 0x06 }, + { CCI_REG8(0x05), 0x07 }, + { CCI_REG8(0x06), 0x44 }, + { CCI_REG8(0x07), 0x08 }, + { CCI_REG8(0x1b), 0x01 }, + { CCI_REG8(0x24), 0xff }, + { CCI_REG8(0x32), 0x03 }, + { CCI_REG8(0x42), 0x5d }, + { CCI_REG8(0x43), 0x08 }, + { CCI_REG8(0x44), 0x81 }, + { CCI_REG8(0x46), 0x5f }, + { CCI_REG8(0x48), 0x18 }, + { CCI_REG8(0x49), 0x04 }, + { CCI_REG8(0x5c), 0x18 }, + { CCI_REG8(0x5e), 0x13 }, + { CCI_REG8(0x70), 0x15 }, + { CCI_REG8(0x77), 0x35 }, + { CCI_REG8(0x79), 0x00 }, + { CCI_REG8(0x7b), 0x08 }, + { CCI_REG8(0x7d), 0x08 }, + { CCI_REG8(0x7e), 0x08 }, + { CCI_REG8(0x7f), 0x08 }, + { CCI_REG8(0x90), 0x37 }, + { CCI_REG8(0x91), 0x05 }, + { CCI_REG8(0x92), 0x18 }, + { CCI_REG8(0x93), 0x27 }, + { CCI_REG8(0x94), 0x05 }, + { CCI_REG8(0x95), 0x38 }, + { CCI_REG8(0x9b), 0x00 }, + { CCI_REG8(0x9c), 0x06 }, + { CCI_REG8(0x9d), 0x28 }, + { CCI_REG8(0x9e), 0x06 }, + { CCI_REG8(0xb2), 0x0f }, + { CCI_REG8(0xb3), 0x29 }, + { CCI_REG8(0xbf), 0x3c }, + { CCI_REG8(0xc2), 0x04 }, + { CCI_REG8(0xc4), 0x00 }, + { CCI_REG8(0xca), 0x20 }, + { CCI_REG8(0xcb), 0x20 }, + { CCI_REG8(0xcc), 0x28 }, + { CCI_REG8(0xcd), 0x28 }, + { CCI_REG8(0xce), 0x20 }, + { CCI_REG8(0xcf), 0x20 }, + { CCI_REG8(0xd0), 0x2a }, + { CCI_REG8(0xd1), 0x2a }, + { CCI_REG8(0xfd), 0x0f }, + { CCI_REG8(0x00), 0x00 }, + { CCI_REG8(0x01), 0xa0 }, + { CCI_REG8(0x02), 0x48 }, + { CCI_REG8(0x07), 0x8f }, + { CCI_REG8(0x08), 0x70 }, + { CCI_REG8(0x09), 0x01 }, + { CCI_REG8(0x0b), 0x40 }, + { CCI_REG8(0x0d), 0x07 }, + { CCI_REG8(0x11), 0x33 }, + { CCI_REG8(0x12), 0x77 }, + { CCI_REG8(0x13), 0x66 }, + { CCI_REG8(0x14), 0x65 }, + { CCI_REG8(0x15), 0x37 }, + { CCI_REG8(0x16), 0xbf }, + { CCI_REG8(0x17), 0xff }, + { CCI_REG8(0x18), 0xff }, + { CCI_REG8(0x19), 0x12 }, + { CCI_REG8(0x1a), 0x10 }, + { CCI_REG8(0x1c), 0x77 }, + { CCI_REG8(0x1d), 0x77 }, + { CCI_REG8(0x20), 0x0f }, + { CCI_REG8(0x21), 0x0f }, + { CCI_REG8(0x22), 0x0f }, + { CCI_REG8(0x23), 0x0f }, + { CCI_REG8(0x2b), 0x20 }, + { CCI_REG8(0x2c), 0x20 }, + { CCI_REG8(0x2d), 0x04 }, + { CCI_REG8(0xfd), 0x03 }, + { CCI_REG8(0x9d), 0x0f }, + { CCI_REG8(0x9f), 0x40 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x1b }, + { CCI_REG8(0xfd), 0x04 }, + { CCI_REG8(0x19), 0x60 }, + { CCI_REG8(0xfd), 0x02 }, + { CCI_REG8(0x75), 0x05 }, + { CCI_REG8(0x7f), 0x06 }, + { CCI_REG8(0x9a), 0x03 }, + { CCI_REG8(0xa2), 0x07 }, + { CCI_REG8(0xa3), 0x10 }, + { CCI_REG8(0xa5), 0x02 }, + { CCI_REG8(0xa6), 0x0b }, + { CCI_REG8(0xa7), 0x48 }, + { CCI_REG8(0xfd), 0x07 }, + { CCI_REG8(0x42), 0x00 }, + { CCI_REG8(0x43), 0x80 }, + { CCI_REG8(0x44), 0x00 }, + { CCI_REG8(0x45), 0x80 }, + { CCI_REG8(0x46), 0x00 }, + { CCI_REG8(0x47), 0x80 }, + { CCI_REG8(0x48), 0x00 }, + { CCI_REG8(0x49), 0x80 }, + { CCI_REG8(0x00), 0xf7 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0xe7), 0x03 }, + { CCI_REG8(0xe7), 0x00 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x93), 0x18 }, + { CCI_REG8(0x94), 0xff }, + { CCI_REG8(0x95), 0xbd }, + { CCI_REG8(0x96), 0x1a }, + { CCI_REG8(0x98), 0x04 }, + { CCI_REG8(0x99), 0x08 }, + { CCI_REG8(0x9b), 0x10 }, + { CCI_REG8(0x9c), 0x3f }, + { CCI_REG8(0xa1), 0x05 }, + { CCI_REG8(0xa4), 0x2f }, + { CCI_REG8(0xc0), 0x0c }, + { CCI_REG8(0xc1), 0x08 }, + { CCI_REG8(0xc2), 0x00 }, + { CCI_REG8(0xb6), 0x20 }, + { CCI_REG8(0xbb), 0x80 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0xa0), 0x01 }, + { CCI_REG8(0xfd), 0x01 }, +}; + +static const struct cci_reg_sequence mode_OV05C10_stream_on_regs[] =3D { + { CCI_REG8(0xfd), 0x01 }, + { CCI_REG8(0x33), 0x03 }, + { CCI_REG8(0x01), 0x02 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x1f }, + { CCI_REG8(0xfd), 0x01 }, +}; + +static const struct cci_reg_sequence mode_OV05C10_stream_off_regs[] =3D { + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x5b }, + { CCI_REG8(0xfd), 0x01 }, + { CCI_REG8(0x33), 0x02 }, + { CCI_REG8(0x01), 0x02 }, +}; + +static const char * const ov05c10_test_pattern_menu[] =3D { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Configurations for supported link frequencies */ +#define OV05C10_LINK_FREQ_900MHZ (900 * HZ_PER_MHZ) + +/* Number of lanes supported */ +#define OV05C10_DATA_LANES 2 + +/* Bits per sample of sensor output */ +#define OV05C10_BITS_PER_SAMPLE 10 + +/* + * pixel_rate =3D link_freq * data-rate * nr_of_lanes / bits_per_sample + * data rate =3D> double data rate; number of lanes =3D> 2; bits per pixel= =3D> 10 + */ +static u64 link_freq_to_pixel_rate(u64 f, u32 lane_nr) +{ + f *=3D 2 * lane_nr; + do_div(f, OV05C10_BITS_PER_SAMPLE); + + return f; +} + +/* Menu items for LINK_FREQ V4L2 control */ +static const s64 ov05c10_link_freq_menu_items[] =3D { + OV05C10_LINK_FREQ_900MHZ, +}; + +/* Mode configs, currently, only support 1 mode */ +static const struct ov05c10_mode supported_mode =3D { + .width =3D MODE_WIDTH, + .height =3D MODE_HEIGHT, + .vts_def =3D OV05C10_VTS_30FPS, + .vts_min =3D OV05C10_VTS_30FPS, + .hts =3D 640, + .lanes =3D 2, + .reg_list =3D { + .num_of_regs =3D ARRAY_SIZE(ov05c10_2888x1808_regs), + .regs =3D ov05c10_2888x1808_regs, + }, + .link_freq_index =3D OV05C10_LINK_FREQ_900MHZ_INDEX, +}; + +struct ov05c10 { + struct v4l2_subdev sd; + struct media_pad pad; + + /* V4L2 control handler */ + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + struct regmap *regmap; + + /* gpio descriptor */ + struct gpio_desc *enable_gpio; + + /* Current page for sensor register control */ + int cur_page; +}; + +#define to_ov05c10(_sd) container_of(_sd, struct ov05c10, sd) + +static int ov05c10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *frame_fmt; + struct v4l2_subdev_format fmt =3D { + .which =3D V4L2_SUBDEV_FORMAT_TRY, + .format =3D { + .width =3D MODE_WIDTH, + .height =3D MODE_HEIGHT, + .code =3D MEDIA_BUS_FMT_SGRBG10_1X10, + .field =3D V4L2_FIELD_NONE, + } + }; + + frame_fmt =3D v4l2_subdev_state_get_format(sd_state, 0); + *frame_fmt =3D fmt.format; + return 0; +} + +static int ov05c10_switch_page(struct ov05c10 *ov05c10, u32 page, int *err) +{ + int ret =3D 0; + + if (err && *err) + return *err; + + if (page !=3D ov05c10->cur_page) { + cci_write(ov05c10->regmap, OV05C10_REG_PAGE_CTL, page, &ret); + if (!ret) + ov05c10->cur_page =3D page; + } + + if (err) + *err =3D ret; + + return ret; +} + +/* refer to the implementation of cci_read */ +static int ov05c10_reg_read(struct ov05c10 *ov05c10, u32 reg, + u64 *val, int *err) +{ + u32 page; + u32 addr; + int ret =3D 0; + + if (err && *err) + return *err; + + page =3D OV05C10_GET_PAGE_NUM(reg); + addr =3D OV05C10_GET_REG_ADDR(reg); + ov05c10_switch_page(ov05c10, page, &ret); + cci_read(ov05c10->regmap, addr, val, &ret); + if (err) + *err =3D ret; + + return ret; +} + +/* refer to the implementation of cci_write */ +static int ov05c10_reg_write(struct ov05c10 *ov05c10, u32 reg, + u64 val, int *err) +{ + u32 page; + u32 addr; + int ret =3D 0; + + if (err && *err) + return *err; + + page =3D OV05C10_GET_PAGE_NUM(reg); + addr =3D OV05C10_GET_REG_ADDR(reg); + ov05c10_switch_page(ov05c10, page, &ret); + cci_write(ov05c10->regmap, addr, val, &ret); + if (err) + *err =3D ret; + + return ret; +} + +static int ov05c10_update_vblank(struct ov05c10 *ov05c10, u32 vblank) +{ + const struct ov05c10_mode *mode =3D &supported_mode; + u64 val; + int ret =3D 0; + + val =3D mode->height + vblank; + ov05c10_reg_write(ov05c10, OV05C10_REG_VTS, val, &ret); + ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER, + OV05C_REG_TRIGGER_START, &ret); + + return ret; +} + +static int ov05c10_update_exposure(struct ov05c10 *ov05c10, u32 exposure) +{ + int ret =3D 0; + + ov05c10_reg_write(ov05c10, OV05C10_REG_EXPOSURE, exposure, &ret); + ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER, + OV05C_REG_TRIGGER_START, &ret); + + return ret; +} + +static int ov05c10_update_analog_gain(struct ov05c10 *ov05c10, u32 a_gain) +{ + int ret =3D 0; + + ov05c10_reg_write(ov05c10, OV05C10_REG_ANALOG_GAIN, a_gain, &ret); + ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER, + OV05C_REG_TRIGGER_START, &ret); + + return ret; +} + +static int ov05c10_update_digital_gain(struct ov05c10 *ov05c10, u32 d_gain) +{ + u64 val; + int ret =3D 0; + + val =3D d_gain & OV05C10_DGTL_GAIN_L_MASK; + ov05c10_reg_write(ov05c10, OV05C10_REG_DGTL_GAIN_L, val, &ret); + + val =3D (d_gain & OV05C10_DGTL_GAIN_H_MASK) >> OV05C10_DGTL_GAIN_H_SHIFT; + ov05c10_reg_write(ov05c10, OV05C10_REG_DGTL_GAIN_H, val, &ret); + + ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER, + OV05C_REG_TRIGGER_START, &ret); + + return ret; +} + +static int ov05c10_enable_test_pattern(struct ov05c10 *ov05c10, u32 patter= n) +{ + u64 val; + int ret =3D 0; + + if (pattern) { + ov05c10_reg_read(ov05c10, OV05C10_REG_TEST_PATTERN_CTL, + &val, &ret); + ov05c10_reg_write(ov05c10, OV05C10_REG_TEST_PATTERN_CTL, + val | OV05C10_REG_TEST_PATTERN_XXX, &ret); + ov05c10_reg_read(ov05c10, OV05C10_REG_TEST_PATTERN, &val, &ret); + val |=3D OV05C10_TEST_PATTERN_ENABLE; + } else { + ov05c10_reg_read(ov05c10, OV05C10_REG_TEST_PATTERN, &val, &ret); + val &=3D ~OV05C10_TEST_PATTERN_ENABLE; + } + + ov05c10_reg_write(ov05c10, OV05C10_REG_TEST_PATTERN, val, &ret); + ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER, + OV05C_REG_TRIGGER_START, &ret); + + return ret; +} + +static int ov05c10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov05c10 *ov05c10 =3D container_of(ctrl->handler, + struct ov05c10, ctrl_handler); + struct i2c_client *client =3D v4l2_get_subdevdata(&ov05c10->sd); + const struct ov05c10_mode *mode =3D &supported_mode; + s64 max; + int ret =3D 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id =3D=3D V4L2_CID_VBLANK) { + s64 cur_exp =3D ov05c10->exposure->cur.val; + + /* Update max exposure while meeting expected vblanking */ + max =3D mode->height + ctrl->val - OV05C10_EXPOSURE_MAX_MARGIN; + cur_exp =3D clamp(cur_exp, ov05c10->exposure->minimum, max); + ret =3D __v4l2_ctrl_modify_range(ov05c10->exposure, + ov05c10->exposure->minimum, + max, ov05c10->exposure->step, + cur_exp); + if (!ret) + return ret; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret =3D ov05c10_update_analog_gain(ov05c10, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret =3D ov05c10_update_digital_gain(ov05c10, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret =3D ov05c10_update_exposure(ov05c10, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret =3D ov05c10_update_vblank(ov05c10, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret =3D ov05c10_enable_test_pattern(ov05c10, ctrl->val); + break; + default: + ret =3D -ENOTTY; + dev_err(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov05c10_ctrl_ops =3D { + .s_ctrl =3D ov05c10_set_ctrl, +}; + +static int ov05c10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order(GRBG) is supported */ + if (code->index > 0) + return -EINVAL; + + code->code =3D MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov05c10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + /* ov05c10 driver currently only supports 1 mode*/ + if (fse->index !=3D 0) + return -EINVAL; + + if (fse->code !=3D MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width =3D supported_mode.width; + fse->max_width =3D fse->min_width; + fse->min_height =3D supported_mode.height; + fse->max_height =3D fse->min_height; + + return 0; +} + +static void ov05c10_update_pad_format(const struct ov05c10_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width =3D mode->width; + fmt->format.height =3D mode->height; + fmt->format.code =3D MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field =3D V4L2_FIELD_NONE; +} + +static int ov05c10_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + const struct ov05c10_mode *mode; + s32 vblank_def; + s32 vblank_min; + s64 pixel_rate; + s64 link_freq; + s64 h_blank; + + /* Only one raw bayer(GRBG) order is supported */ + if (fmt->format.code !=3D MEDIA_BUS_FMT_SGRBG10_1X10) + fmt->format.code =3D MEDIA_BUS_FMT_SGRBG10_1X10; + + mode =3D &supported_mode; + ov05c10_update_pad_format(mode, fmt); + if (fmt->which =3D=3D V4L2_SUBDEV_FORMAT_TRY) { + framefmt =3D v4l2_subdev_state_get_format(sd_state, fmt->pad); + *framefmt =3D fmt->format; + } else { + __v4l2_ctrl_s_ctrl(ov05c10->link_freq, mode->link_freq_index); + link_freq =3D ov05c10_link_freq_menu_items[mode->link_freq_index]; + pixel_rate =3D link_freq_to_pixel_rate(link_freq, + mode->lanes); + __v4l2_ctrl_s_ctrl_int64(ov05c10->pixel_rate, pixel_rate); + + /* Update limits and set FPS to default */ + vblank_def =3D mode->vts_def - mode->height; + vblank_min =3D mode->vts_min - mode->height; + __v4l2_ctrl_modify_range(ov05c10->vblank, vblank_min, + OV05C10_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov05c10->vblank, vblank_def); + h_blank =3D mode->hts; + __v4l2_ctrl_modify_range(ov05c10->hblank, h_blank, + h_blank, 1, h_blank); + } + + return 0; +} + +static int ov05c10_start_streaming(struct ov05c10 *ov05c10) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&ov05c10->sd); + const struct ov05c10_mode *mode =3D &supported_mode; + const struct ov05c10_reg_list *reg_list; + int ret =3D 0; + + /* Apply default values of current mode */ + reg_list =3D &mode->reg_list; + cci_multi_reg_write(ov05c10->regmap, reg_list->regs, + reg_list->num_of_regs, &ret); + if (ret) { + dev_err(&client->dev, "fail to set mode, ret: %d\n", ret); + return ret; + } + + /* Apply customized values from user */ + ret =3D __v4l2_ctrl_handler_setup(ov05c10->sd.ctrl_handler); + if (ret) { + dev_err(&client->dev, "failed to setup v4l2 handler %d\n", ret); + return ret; + } + + cci_multi_reg_write(ov05c10->regmap, mode_OV05C10_stream_on_regs, + ARRAY_SIZE(mode_OV05C10_stream_on_regs), &ret); + if (ret) + dev_err(&client->dev, "fail to start the streaming\n"); + + return ret; +} + +static int ov05c10_stop_streaming(struct ov05c10 *ov05c10) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&ov05c10->sd); + int ret =3D 0; + + cci_multi_reg_write(ov05c10->regmap, mode_OV05C10_stream_off_regs, + ARRAY_SIZE(mode_OV05C10_stream_off_regs), &ret); + if (ret) + dev_err(&client->dev, "fail to stop the streaming\n"); + + return ret; +} + +static void ov05c10_sensor_power_set(struct ov05c10 *ov05c10, bool on) +{ + if (on) { + gpiod_set_value(ov05c10->enable_gpio, 0); + usleep_range(10, 20); + + gpiod_set_value(ov05c10->enable_gpio, 1); + usleep_range(1000, 2000); + } else { + gpiod_set_value(ov05c10->enable_gpio, 0); + usleep_range(10, 20); + } +} + +static int ov05c10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(sd); + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + int ret =3D 0; + + ret =3D pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + ov05c10->cur_page =3D -1; + + ret =3D ov05c10_start_streaming(ov05c10); + if (ret) + goto err_rpm_put; + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static int ov05c10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(sd); + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + + ov05c10_stop_streaming(ov05c10); + pm_runtime_put(&client->dev); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov05c10_video_ops =3D { + .s_stream =3D v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov05c10_pad_ops =3D { + .enum_mbus_code =3D ov05c10_enum_mbus_code, + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D ov05c10_set_pad_format, + .enum_frame_size =3D ov05c10_enum_frame_size, + .enable_streams =3D ov05c10_enable_streams, + .disable_streams =3D ov05c10_disable_streams, +}; + +static const struct v4l2_subdev_ops ov05c10_subdev_ops =3D { + .video =3D &ov05c10_video_ops, + .pad =3D &ov05c10_pad_ops, +}; + +static const struct media_entity_operations ov05c10_subdev_entity_ops =3D { + .link_validate =3D v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov05c10_internal_ops =3D { + .init_state =3D ov05c10_init_state, +}; + +static int ov05c10_init_controls(struct ov05c10 *ov05c10) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&ov05c10->sd); + const struct ov05c10_mode *mode =3D &supported_mode; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 pixel_rate_max; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + u32 max_items; + s64 hblank; + int ret; + + ret =3D v4l2_ctrl_handler_init(&ov05c10->ctrl_handler, 10); + if (ret) + return ret; + + ctrl_hdlr =3D &ov05c10->ctrl_handler; + + max_items =3D ARRAY_SIZE(ov05c10_link_freq_menu_items) - 1; + ov05c10->link_freq =3D + v4l2_ctrl_new_int_menu(ctrl_hdlr, + NULL, + V4L2_CID_LINK_FREQ, + max_items, + 0, + ov05c10_link_freq_menu_items); + if (ov05c10->link_freq) + ov05c10->link_freq->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate_max =3D + link_freq_to_pixel_rate(ov05c10_link_freq_menu_items[0], + supported_mode.lanes); + ov05c10->pixel_rate =3D v4l2_ctrl_new_std(ctrl_hdlr, NULL, + V4L2_CID_PIXEL_RATE, + 0, pixel_rate_max, + 1, pixel_rate_max); + + vblank_def =3D mode->vts_def - mode->height; + vblank_min =3D mode->vts_min - mode->height; + ov05c10->vblank =3D v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_VBLANK, + vblank_min, + OV05C10_VTS_MAX - mode->height, + 1, vblank_def); + + hblank =3D (mode->hts > mode->width) ? (mode->hts - mode->width) : 0; + ov05c10->hblank =3D v4l2_ctrl_new_std(ctrl_hdlr, NULL, + V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); + if (ov05c10->hblank) + ov05c10->hblank->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + exposure_max =3D mode->vts_def - OV05C10_EXPOSURE_MAX_MARGIN; + ov05c10->exposure =3D v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV05C10_EXPOSURE_MIN, + exposure_max, + OV05C10_EXPOSURE_STEP, + exposure_max); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV05C10_ANA_GAIN_MIN, OV05C10_ANA_GAIN_MAX, + OV05C10_ANA_GAIN_STEP, OV05C10_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV05C10_DGTL_GAIN_MIN, OV05C10_DGTL_GAIN_MAX, + OV05C10_DGTL_GAIN_STEP, OV05C10_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov05c10_test_pattern_menu) - 1, + 0, 0, ov05c10_test_pattern_menu); + + if (ctrl_hdlr->error) { + ret =3D ctrl_hdlr->error; + dev_err(&client->dev, "V4L2 control init failed (%d)\n", ret); + goto err_hdl_free; + } + + ret =3D v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto err_hdl_free; + + ret =3D v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov05c10_ctrl_ops, + &props); + if (ret) + goto err_hdl_free; + + ov05c10->sd.ctrl_handler =3D ctrl_hdlr; + + return 0; + +err_hdl_free: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int ov05c10_parse_endpoint(struct device *dev, + struct fwnode_handle *fwnode) +{ + struct v4l2_fwnode_endpoint bus_cfg =3D { + .bus_type =3D V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + unsigned long bitmap; + int ret; + + ep =3D fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(dev, "Failed to get next endpoint\n"); + return -ENXIO; + } + + ret =3D v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes !=3D supported_mode.lanes) { + dev_err(dev, + "number of CSI2 data lanes %d is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret =3D -EINVAL; + goto err_endpoint_free; + } + + ret =3D v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + ov05c10_link_frequencies, + ARRAY_SIZE(ov05c10_link_frequencies), + &bitmap); + if (ret) + dev_err(dev, "v4l2_link_freq_to_bitmap fail with %d\n", ret); +err_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov05c10_probe(struct i2c_client *client) +{ + struct ov05c10 *ov05c10; + u32 clkfreq; + int ret; + + ov05c10 =3D devm_kzalloc(&client->dev, sizeof(*ov05c10), GFP_KERNEL); + if (!ov05c10) + return -ENOMEM; + + struct fwnode_handle *fwnode =3D dev_fwnode(&client->dev); + + ret =3D fwnode_property_read_u32(fwnode, "clock-frequency", &clkfreq); + if (ret) + return dev_err_probe(&client->dev, -EINVAL, + "fail to get clock freq\n"); + if (clkfreq !=3D OV05C10_REF_CLK) + return dev_err_probe(&client->dev, -EINVAL, + "fail invalid clock freq %u, %lu expected\n", + clkfreq, OV05C10_REF_CLK); + + ret =3D ov05c10_parse_endpoint(&client->dev, fwnode); + if (ret) + return dev_err_probe(&client->dev, -EINVAL, + "fail to parse endpoint\n"); + + ov05c10->enable_gpio =3D devm_gpiod_get(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(ov05c10->enable_gpio)) + return dev_err_probe(&client->dev, + PTR_ERR(ov05c10->enable_gpio), + "fail to get enable gpio\n"); + + v4l2_i2c_subdev_init(&ov05c10->sd, client, &ov05c10_subdev_ops); + + ov05c10->regmap =3D devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(ov05c10->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(ov05c10->regmap), + "fail to init cci\n"); + + ov05c10->cur_page =3D -1; + + ret =3D ov05c10_init_controls(ov05c10); + if (ret) + return dev_err_probe(&client->dev, ret, "fail to init ctl\n"); + + ov05c10->sd.internal_ops =3D &ov05c10_internal_ops; + ov05c10->sd.flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE; + ov05c10->sd.entity.ops =3D &ov05c10_subdev_entity_ops; + ov05c10->sd.entity.function =3D MEDIA_ENT_F_CAM_SENSOR; + + ov05c10->pad.flags =3D MEDIA_PAD_FL_SOURCE; + + ret =3D media_entity_pads_init(&ov05c10->sd.entity, NUM_OF_PADS, + &ov05c10->pad); + if (ret) + goto err_hdl_free; + + ret =3D v4l2_subdev_init_finalize(&ov05c10->sd); + if (ret < 0) + goto err_media_entity_cleanup; + + ret =3D v4l2_async_register_subdev_sensor(&ov05c10->sd); + if (ret) + goto err_media_entity_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + return 0; + +err_media_entity_cleanup: + media_entity_cleanup(&ov05c10->sd.entity); + +err_hdl_free: + v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler); + + return ret; +} + +static void ov05c10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static int ov05c10_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd =3D dev_get_drvdata(dev); + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + + ov05c10_sensor_power_set(ov05c10, true); + return 0; +} + +static int ov05c10_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd =3D dev_get_drvdata(dev); + struct ov05c10 *ov05c10 =3D to_ov05c10(sd); + + ov05c10_sensor_power_set(ov05c10, false); + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov05c10_pm_ops, ov05c10_runtime_suspend, + ov05c10_runtime_resume, NULL); + +static const struct i2c_device_id ov05c10_i2c_ids[] =3D { + {"ov05c10", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov05c10_i2c_ids); + +static struct i2c_driver ov05c10_i2c_driver =3D { + .driver =3D { + .name =3D DRV_NAME, + .pm =3D pm_ptr(&ov05c10_pm_ops), + }, + .id_table =3D ov05c10_i2c_ids, + .probe =3D ov05c10_probe, + .remove =3D ov05c10_remove, +}; + +module_i2c_driver(ov05c10_i2c_driver); + +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_AUTHOR("Venkata Narendra Kumar Gutta "); +MODULE_AUTHOR("Bin Du "); +MODULE_DESCRIPTION("OmniVision OV05C1010 sensor driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0