From nobody Wed Dec 17 10:46:08 2025 Received: from mail-lj1-f175.google.com (mail-lj1-f175.google.com [209.85.208.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F2652836B1 for ; Mon, 3 Nov 2025 14:56:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762181815; cv=none; b=EMY1l0k4nNlpuL7pBz9uizM784uhr9wOqgAH6xDT6Sd7hUnqDJhajl66eC0BEGcO98vAwm9GPY0dBBeUpanZTkGUsbU8LsMO8eDo1+JggNlCNf+78qd+FFM3a22Y/n5AzaDz7DAoWXCwLfYTpaemBxKbs2IyEOTFK6BJSG4HIpE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762181815; c=relaxed/simple; bh=Cwc487Q7TuN+iZw77LSuYBkO6zlPKMprgNtjBsX8sk4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QmBgHZI96swCYcLKUUppFvIUjuT+CP4cU7bDMY1oDZZF+VyAPgpI3h67LlogQ5hQLiOGKOUK9pfbf8XOx4+JzTW33Turlt5uNGRaGcxa7fG/xkBhcShihaNogQVHW9jWKtzQIrTViNWbat+63TTEWgcqMCKPvlinSytFSGcscP4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=BPr2eF4T; arc=none smtp.client-ip=209.85.208.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BPr2eF4T" Received: by mail-lj1-f175.google.com with SMTP id 38308e7fff4ca-378d710caedso40696561fa.3 for ; Mon, 03 Nov 2025 06:56:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762181810; x=1762786610; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uRJylgA5tDh6J0HyV09+y5kblN0Saxh3CxXH4UQ+jfc=; b=BPr2eF4TjcjIxpq41SCmA4OJzGueFBpbSRuueOmCVKZc31GYxWEw4hxxrqd81aHkn8 JLpQAqrvzNuR5xt9FYRxCplPZMbzNnIArons6dSp9EreobQr9XNzn38fTE8WBh+83fyM xYuZresJJ8EYu7q2rWorFazzWfxdcjUai9eg4ppxl++87UNb5EuZR5vRiXe/Dq8BDgP+ QnVfWRpyMjrRXN+2Q4viUGC4SzEoz8lwlRhbr3mVb+kif3plTGnUcZvZC4DF/m/mVm/t SpN/4UvFsWEaYiMlpSejUwKdIcGgF0EwOHA/I4JvD1Rva5iPYOagfpOt97iGgxN0pVie kIqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762181810; x=1762786610; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uRJylgA5tDh6J0HyV09+y5kblN0Saxh3CxXH4UQ+jfc=; b=CyhQipnsoyTtCAWTSFuSi52HCZjbItlWYYUZgIbXRUrDMgyGhKgoPj52HRKxQA6OiS 6rppO9czeHIRi6TdNtGZ4rFVo3vYG37ycnF+G8HspffNhNcgWvBLu9GP2ZshhpVfbQrs hkciDyY9Y/dD01obOIzdNAJ6XjmiQObRNd3iTGSI877NzV6Miaf6K4tVrCeeq02Qis4J vLZav7K+23yYABAd8WaziyklfX7Iok1eOG/iEgP7ux3OhrVZ19rdmkB2dGbpvyC4J/eV VPS4oMkRGgFZVSDeQ3CrfSYRKQ5EXq1esPvn/JonJx/d324GSVRaHPvsOAf+vZ556lhl DfJQ== X-Forwarded-Encrypted: i=1; AJvYcCWNhE9iHPk2Q4lKN6TDkisZHuEMFxvBH0TsAykHsdywfltSXnGDoiPolubj1fPXGmzsswor8zXj3SSk/xE=@vger.kernel.org X-Gm-Message-State: AOJu0YwQ44+loFtFMHaMy+ltYKRxUQpu+E4j3Tjn9bVius5tqmjOhQpM fNjhoJjDt+3k5cC/5khWREsx463BZj9UV0XADAD+qFSYapNlMqeKAEuT X-Gm-Gg: ASbGnct0toQfxjhPeH1cR7gayRNXYhXnkhr26qnv/5TPW+xdFXsk//ukO914TW4/ai4 C9StOnkcUw6RCjVv2BWnBE0kljZJEqsFyZ7WThQ2q5U7cIk2pVkU3Qh/LxEhqCoKmQGR2xMbysG MydQWXNkBSIs0oAZtYIynuKF5LCMmYLHpLkA5wPnTGqbAaR6m4Xk41awLq4o6Ac3i7sdDhfWvl2 b+/eTryM/IBUkYWMbV7t7RKBEYF5Tx7XbfIO54DcjhC4vkCeg22dKYuvrvG5SQeDe5W5zifAM+W 2L1+vgBtsUZsmkKPxZq7oFDZnk5fd+VzhRQaDYH2UyrCjy24CZTZ3EdieUOlUkovJ+src5yx/zU GS0NGNZN82Qe9umlvG79WfHzz7CWeud+IbrCw0/HGiPuN2FyXTzP1jDAT3AmbEuYos3jc+mcFce LO X-Google-Smtp-Source: AGHT+IGJeEBnjP4R5psIFnDlYx1WzqziKeHDlwCz84CqG5SR2VgxHltzzwJIDJwksgTuiJMuglMA9Q== X-Received: by 2002:a2e:b8c1:0:b0:37a:2c11:2c7b with SMTP id 38308e7fff4ca-37a2c11330dmr21136541fa.18.1762181810210; Mon, 03 Nov 2025 06:56:50 -0800 (PST) Received: from xeon ([188.163.112.61]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-37a414d58b3sm937061fa.18.2025.11.03.06.56.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Nov 2025 06:56:49 -0800 (PST) From: Svyatoslav Ryhel To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Svyatoslav Ryhel , Hans Verkuil , Hans de Goede , =?UTF-8?q?Andr=C3=A9=20Apitzsch?= , Sylvain Petinot , Benjamin Mugnier , Dongcheng Yan , Heimir Thor Sverrisson , Tarang Raval Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 1/2] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor Date: Mon, 3 Nov 2025 16:56:28 +0200 Message-ID: <20251103145629.21588-2-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251103145629.21588-1-clamor95@gmail.com> References: <20251103145629.21588-1-clamor95@gmail.com> 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 Content-Type: text/plain; charset="utf-8" Add bindings for Sony IMX111 CMOS Digital Image Sensor found in LG Optimus 4X (P880) and Optimus Vu (P895) smartphones. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Conor Dooley --- .../bindings/media/i2c/sony,imx111.yaml | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx111= .yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b= /Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml new file mode 100644 index 000000000000..20f48d5e9b2d --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx111.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX111 8MP CMOS Digital Image Sensor + +maintainers: + - Svyatoslav Ryhel + +description: + IMX111 sensor is a Sony CMOS active pixel digital image sensor with an a= ctive + array size of 2464H x 3280V. It is programmable through I2C interface. I= mage + data is sent through MIPI CSI-2, through 1 or 2 lanes. + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + - $ref: /schemas/nvmem/nvmem-consumer.yaml# + +properties: + compatible: + const: sony,imx111 + + reg: + maxItems: 1 + + clocks: + description: EXTCLK with possible frequency from 6 to 54 MHz + maxItems: 1 + + reset-gpios: + maxItems: 1 + + iovdd-supply: + description: Digital IO power supply (1.8V) + + dvdd-supply: + description: Digital power supply (1.2V) + + avdd-supply: + description: Analog power supply (2.7V) + + port: + additionalProperties: false + $ref: /schemas/graph.yaml#/$defs/port-base + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - port + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + camera@10 { + compatible =3D "sony,imx111"; + reg =3D <0x10>; + + clocks =3D <&imx111_clk>; + + iovdd-supply =3D <&camera_vddio_1v8>; + dvdd-supply =3D <&camera_vddd_1v2>; + avdd-supply =3D <&camera_vdda_2v7>; + + orientation =3D <1>; + rotation =3D <90>; + + nvmem =3D <&eeprom>; + flash-leds =3D <&led>; + lens-focus =3D <&vcm>; + + reset-gpios =3D <&gpio 84 GPIO_ACTIVE_LOW>; + + port { + imx111_output: endpoint { + data-lanes =3D <1 2>; + link-frequencies =3D /bits/ 64 <542400000>; + remote-endpoint =3D <&csi_input>; + }; + }; + }; + }; +... --=20 2.51.0 From nobody Wed Dec 17 10:46:08 2025 Received: from mail-lj1-f169.google.com (mail-lj1-f169.google.com [209.85.208.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C0B73148B5 for ; Mon, 3 Nov 2025 14:56:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762181819; cv=none; b=YEK5gQFQD4OZ5weUbLhQMyQ5vQFFweJ/i54twYkwP63bevYQ3OAXqhtjXcYp4OGD5Ljh9nTX6f+aESNO/VMLdsrKxXYOdCDGPR9071PgcDHfMd6v24uUBS5C4Wke41D7SmOv7oolSIhpbivZx3e9w1wj01mlMNQukT0sstgNdcg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762181819; c=relaxed/simple; bh=gRszQmdNIcwrHWVfMvIhMpkZEs89oVXzvoCi26nQuLY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TZ/DOf37ObTIzT+5b2S+Xj/yyFYkq0xkX0eCmnNOQP8iMKK5yoe1cN4WO0z1YMY13uyyHOt9/bTnlecqotS2eDJRLD3ykChQra//ibFGQ3bRg+bZ+BAIyvX+Tcnn4QpTz61M4lK7CTlYROEiDgucni4OQZBioK2Ef0gfOA+l2aM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Xt1ABgxO; arc=none smtp.client-ip=209.85.208.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Xt1ABgxO" Received: by mail-lj1-f169.google.com with SMTP id 38308e7fff4ca-37a33b06028so13418821fa.2 for ; Mon, 03 Nov 2025 06:56:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762181813; x=1762786613; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=CNS0o9QTsjqHW3SKbqvahJIP6G9kv2wGh8faUMoSg78=; b=Xt1ABgxOK7RusSQt/cupu5UN4Db3lJYESyAchmQxpytAo2Q9F9icUP0GbegCHDFk21 kjvcVodVQ8SmZmV5uNC79P3lbNa6mxyTX38LGRPH6OMvTqFVtaJg86XxrfGXTOT3ATXd txl8O9PPi9c9QOpPioNDUovXbP0e8vo5gUjfX//iacNwVKuleT901V4u6XWQBXYjpXGN S1S5t0vrvSlaAHkCeKcwPU2EodRH5Jh9zlxOyZrCgXtHgEb1U83Q8leW+YUaYeYuBAB7 2L3roTbVDVf0oRw34fBwdBx2Wz5zbK30l98CmxDSzGewZf3gQ6J9DwyIVR1+ZcIX3NfL PmDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762181813; x=1762786613; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CNS0o9QTsjqHW3SKbqvahJIP6G9kv2wGh8faUMoSg78=; b=fzO2jvtjbbdXgtNNMfQ6rgg8utP5jDNp3c2tobR30H1Lr8aYouzgc7qHE/EXrUMeJP AV7vk37aFoCeibz1p3nhBKZotR1cmzadeWiFJdYKygsEMe4og73ZgHljW2vzphm8/E1u 501Q5BtbVhk6mYMzka6lJogwKgid3TogCxI1ho8lvOJFVrgivy6HO1WXPdOLy7O9e5x9 f2naZUeAJ1H/aBr9B+xj/bRJsK53RFk9oxu/2Twc8EEikanfP+oBZV49WkqlfKiOH/3d YGBj1nAmywoYY0pAWce/Hv3RTD/ZKdSxwhiQMaxs8PY+MPRGD4womgMZCMtzC7b/Cy5/ 7XLw== X-Forwarded-Encrypted: i=1; AJvYcCUipeZdExJ83PcB9prztNB+VoSX+e171LdsjDy/FzrKaxRh3NJsZ7cUjhglfwDSdWupbRzB3MbzIb7xiik=@vger.kernel.org X-Gm-Message-State: AOJu0YwiQ5zYtqiFTgaSzUkJLmJ8yr7v8ccvzCSj3kM9ohwV2M8LKl8y QINytUpxRyz1shPNUYPEu085KfhUPs6x5vWBAI7n6vcXqHuzma+ZKuin X-Gm-Gg: ASbGncvsev/ty9wYnde2f2Q19aIB4iL15D4EmCBDRyK9Wie6dwH5vpfGF4KFiqIQSDZ UlVXrLkBv0xb/qyAkb+w5eq/pgy3HwTHvx+7QUA+iqRX0d5pJ6tVsaqpDsOc/O6iGj5DgCGcfbO fzwhq5JEd7eZOxqttnwq/1XAh6eC0uEJh80rshE6ZvFS9Ovo7nypgj4aq96Js+WpT1vcuFDbUs3 Le+t1OfQSsvTB12oLoUB4zUpWoapEv2tGof0zMD4XYgkSQPTM+kZG/Vf2e+MFuWO71kXuNXh8K0 DRuewwRpJ+jubkeLkuxAv2tOJoMaUJ7SmQQxN+yKNmrXjLA6frQ6t2PxrrAzTe8n/z0a68Dextx FYekfkbIRkYUKagZaxz39G4Sl1Ilqhy0kzNsXWOucPubnhWndwwr2xR+m3qr5TU6a8A== X-Google-Smtp-Source: AGHT+IE/M3wAuPMhMiVTPXZBWZuwOfVUIKVxvxhiEjSgYglFtP13EQpw6QCNhGk380uenZoIe3ULTA== X-Received: by 2002:a05:651c:30ca:b0:37a:2c75:7d83 with SMTP id 38308e7fff4ca-37a2c75825emr19442031fa.13.1762181811859; Mon, 03 Nov 2025 06:56:51 -0800 (PST) Received: from xeon ([188.163.112.61]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-37a414d58b3sm937061fa.18.2025.11.03.06.56.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Nov 2025 06:56:51 -0800 (PST) From: Svyatoslav Ryhel To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Svyatoslav Ryhel , Hans Verkuil , Hans de Goede , =?UTF-8?q?Andr=C3=A9=20Apitzsch?= , Sylvain Petinot , Benjamin Mugnier , Dongcheng Yan , Heimir Thor Sverrisson , Tarang Raval Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 2/2] media: i2c: add Sony IMX111 CMOS camera sensor driver Date: Mon, 3 Nov 2025 16:56:29 +0200 Message-ID: <20251103145629.21588-3-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251103145629.21588-1-clamor95@gmail.com> References: <20251103145629.21588-1-clamor95@gmail.com> 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 Content-Type: text/plain; charset="utf-8" Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a camera sensor using the i2c bus for control and the csi-2 bus for data. The following features are supported: - manual exposure, digital and analog gain control support - pixel rate/link freq control support - supported resolution up to 3280x2464 for single shot capture - supported resolution up to 1920x1080 @ 30fps for video - supported bayer order output SGBRG10 and SGBRG8 Camera module seems to be partially compatible with Nokia SMIA but it lacks a few registers required for clock calculations and has different vendor-specific per-mode configurations which makes it incompatible with existing CCS driver. Signed-off-by: Svyatoslav Ryhel --- drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx111.c | 1610 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1621 insertions(+) create mode 100644 drivers/media/i2c/imx111.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 6237fe804a5c..2283a8d5b54b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -127,6 +127,16 @@ config VIDEO_HI847 To compile this driver as a module, choose M here: the module will be called hi847. =20 +config VIDEO_IMX111 + tristate "Sony IMX111 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for the Sony IMX111 camera + sensors. + + To compile this driver as a module, choose M here: the + module will be called imx111. + config VIDEO_IMX208 tristate "Sony IMX208 sensor support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5873d29433ee..67b810c91870 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_HI556) +=3D hi556.o obj-$(CONFIG_VIDEO_HI846) +=3D hi846.o obj-$(CONFIG_VIDEO_HI847) +=3D hi847.o obj-$(CONFIG_VIDEO_I2C) +=3D video-i2c.o +obj-$(CONFIG_VIDEO_IMX111) +=3D imx111.o obj-$(CONFIG_VIDEO_IMX208) +=3D imx208.o obj-$(CONFIG_VIDEO_IMX214) +=3D imx214.o obj-$(CONFIG_VIDEO_IMX219) +=3D imx219.o diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c new file mode 100644 index 000000000000..c269e9fdcb0b --- /dev/null +++ b/drivers/media/i2c/imx111.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* product information registers */ +#define IMX111_PRODUCT_ID CCI_REG16(0x0000) +#define IMX111_CHIP_ID 0x111 +#define IMX111_REVISION CCI_REG8(0x0002) +#define IMX111_MANUFACTURER_ID CCI_REG8(0x0003) +#define IMX111_FRAME_COUNTER CCI_REG8(0x0005) +#define IMX111_PIXEL_ORDER CCI_REG8(0x0006) + +/* general configuration registers */ +#define IMX111_STREAMING_MODE CCI_REG8(0x0100) +#define IMX111_MODE_STANDBY 0 +#define IMX111_MODE_STREAMING 1 +#define IMX111_IMAGE_ORIENTATION CCI_REG8(0x0101) +#define IMX111_IMAGE_HFLIP BIT(0) +#define IMX111_IMAGE_VFLIP BIT(1) +#define IMX111_SOFTWARE_RESET CCI_REG8(0x0103) +#define IMX111_RESET_ON 1 +#define IMX111_GROUP_WRITE CCI_REG8(0x0104) +#define IMX111_GROUP_WRITE_ON 1 +#define IMX111_FRAME_DROP CCI_REG8(0x0105) +#define IMX111_FRAME_DROP_ON 1 +#define IMX111_CHANNEL_ID CCI_REG8(0x0110) +#define IMX111_SIGNALLING_MODE CCI_REG8(0x0111) +#define IMX111_DATA_DEPTH CCI_REG16(0x0112) +#define IMX111_DATA_DEPTH_RAW8 0x08 +#define IMX111_DATA_DEPTH_RAW10 0x0a + +/* integration time registers */ +#define IMX111_INTEGRATION_TIME CCI_REG16(0x0202) +#define IMX111_INTEGRATION_TIME_MIN 0x1 +#define IMX111_INTEGRATION_TIME_MAX 0xffff +#define IMX111_INTEGRATION_TIME_STEP 1 +#define IMX111_INTEGRATION_TIME_OFFSET 5 + +/* analog gain control */ +#define IMX111_REG_ANALOG_GAIN CCI_REG8(0x0205) +#define IMX111_ANA_GAIN_MIN 0 +#define IMX111_ANA_GAIN_MAX 240 +#define IMX111_ANA_GAIN_STEP 1 +#define IMX111_ANA_GAIN_DEFAULT 0 + +/* digital gain control */ +#define IMX111_REG_DIG_GAIN_GREENR CCI_REG16(0x020e) +#define IMX111_REG_DIG_GAIN_RED CCI_REG16(0x0210) +#define IMX111_REG_DIG_GAIN_BLUE CCI_REG16(0x0212) +#define IMX111_REG_DIG_GAIN_GREENB CCI_REG16(0x0214) +#define IMX111_DGTL_GAIN_MIN 0x0100 +#define IMX111_DGTL_GAIN_MAX 0x0fff +#define IMX111_DGTL_GAIN_DEFAULT 0x0100 +#define IMX111_DGTL_GAIN_STEP 1 + +/* clock configuration registers */ +#define IMX111_PIXEL_CLK_DIVIDER_PLL1 CCI_REG8(0x0301) +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1 CCI_REG8(0x0303) +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1 CCI_REG8(0x0305) +#define IMX111_PLL_MULTIPLIER_PLL1 CCI_REG8(0x0307) +#define IMX111_PLL_SETTLING_TIME CCI_REG8(0x303c) +#define IMX111_PLL_SETTLING_TIME_DEFAULT 200 +#define IMX111_POST_DIVIDER CCI_REG8(0x30a4) +#define IMX111_POST_DIVIDER_DIV1 2 +#define IMX111_POST_DIVIDER_DIV2 0 +#define IMX111_POST_DIVIDER_DIV4 1 + +/* frame timing registers */ +#define IMX111_VERTICAL_TOTAL_LENGTH CCI_REG16(0x0340) +#define IMX111_VTL_MAX 0x09d8 +#define IMX111_VBLANK_MIN 16 +#define IMX111_HORIZONTAL_TOTAL_LENGTH CCI_REG16(0x0342) +#define IMX111_HTL_MAX 0x0dd0 +#define IMX111_HBLANK_MIN 16 + +/* image size registers */ +#define IMX111_HORIZONTAL_START CCI_REG16(0x0344) +#define IMX111_VERTICAL_START CCI_REG16(0x0346) +#define IMX111_HORIZONTAL_END CCI_REG16(0x0348) +#define IMX111_VERTICAL_END CCI_REG16(0x034a) +#define IMX111_IMAGE_WIDTH CCI_REG16(0x034c) +#define IMX111_IMAGE_HEIGHT CCI_REG16(0x034e) +#define IMX111_H_EVEN_INC CCI_REG8(0x0381) +#define IMX111_H_ODD_INC CCI_REG8(0x0383) +#define IMX111_W_EVEN_INC CCI_REG8(0x0385) +#define IMX111_W_ODD_INC CCI_REG8(0x0387) + +/* test pattern registers */ +#define IMX111_TEST_PATTERN CCI_REG8(0x0601) +#define IMX111_TEST_PATTERN_NONE 0 +#define IMX111_TEST_PATTERN_SOLID 1 +#define IMX111_TEST_PATTERN_BARS 2 +#define IMX111_TEST_PATTERN_FADE 3 +#define IMX111_TEST_PATTERN_PN9 4 +#define IMX111_SOLID_COLOR_RED CCI_REG16(0x0602) +#define IMX111_SOLID_COLOR_GR CCI_REG16(0x0604) +#define IMX111_SOLID_COLOR_BLUE CCI_REG16(0x0606) +#define IMX111_SOLID_COLOR_GB CCI_REG16(0x0608) +#define IMX111_TESTP_COLOUR_MIN 0 +#define IMX111_TESTP_COLOUR_MAX 0x03ff +#define IMX111_TESTP_COLOUR_STEP 1 + +#define IMX111_FRAME_RATE_STEP 5 + +#define IMX111_PIXEL_ARRAY_WIDTH 3280U +#define IMX111_PIXEL_ARRAY_HEIGHT 2464U + +enum { + IMX111_MODE_3280x2464, + IMX111_MODE_3280x1848, + IMX111_MODE_3280x1098, + IMX111_MODE_2100x1200, + IMX111_MODE_1952x1098, + IMX111_MODE_1920x1080, + IMX111_MODE_1640x1232, + IMX111_MODE_1440x1080, + IMX111_MODE_1640x924, + IMX111_MODE_1308x736, + IMX111_MODE_1280x720, + IMX111_MODE_820x614, + IMX111_MODE_640x480, +}; + +static const struct regulator_bulk_data imx111_supplies[] =3D { + { .supply =3D "iovdd" }, + { .supply =3D "dvdd" }, + { .supply =3D "avdd" }, +}; + +struct imx111_mode { + u32 width; + u32 height; + + /* Default vertical and horizontal total length */ + u32 vtl_def; + u32 htl_def; + + struct { + const struct cci_reg_sequence *regs; + u32 num_of_regs; + } reg_list; +}; + +struct imx111_pll { + u64 extclk_rate; + u8 pre_div; + u8 mult; +}; + +struct imx111 { + struct regmap *regmap; + + struct clk *extclk; + struct gpio_desc *reset; + struct regulator_bulk_data *supplies; + + struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_subdev sd; + struct media_pad pad; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + /* Current mode */ + const struct imx111_mode *cur_mode; + const struct imx111_pll *pll; + u32 data_depth; + + u64 pixel_clk_raw; + s64 default_link_freq; +}; + +static const struct imx111_pll imx111_pll[] =3D { + { .extclk_rate =3D 6000000, .pre_div =3D 1, .mult =3D 113, }, + { .extclk_rate =3D 12000000, .pre_div =3D 2, .mult =3D 113, }, + { .extclk_rate =3D 13500000, .pre_div =3D 1, .mult =3D 50, }, + { .extclk_rate =3D 18000000, .pre_div =3D 2, .mult =3D 75, }, + { .extclk_rate =3D 24000000, .pre_div =3D 4, .mult =3D 113, }, + { .extclk_rate =3D 27000000, .pre_div =3D 2, .mult =3D 50, }, + { .extclk_rate =3D 36000000, .pre_div =3D 4, .mult =3D 75, }, + { .extclk_rate =3D 54000000, .pre_div =3D 4, .mult =3D 50, }, +}; + +/* + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 imx111_mbus_formats[] =3D { + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, +}; + +static const struct cci_reg_sequence imx111_global_init[] =3D { + { CCI_REG8(0x3080), 0x50 }, + { CCI_REG8(0x3087), 0x53 }, + { CCI_REG8(0x309d), 0x94 }, + { CCI_REG8(0x30b1), 0x03 }, + { CCI_REG8(0x30c6), 0x00 }, + { CCI_REG8(0x30c7), 0x00 }, + { CCI_REG8(0x3115), 0x0b }, + { CCI_REG8(0x3118), 0x30 }, + { CCI_REG8(0x311d), 0x25 }, + { CCI_REG8(0x3121), 0x0a }, + { CCI_REG8(0x3212), 0xf2 }, + { CCI_REG8(0x3213), 0x0f }, + { CCI_REG8(0x3215), 0x0f }, + { CCI_REG8(0x3217), 0x0b }, + { CCI_REG8(0x3219), 0x0b }, + { CCI_REG8(0x321b), 0x0d }, + { CCI_REG8(0x321d), 0x0d }, + { CCI_REG8(0x32aa), 0x11 }, + { CCI_REG8(0x3032), 0x40 }, +}; + +static const struct cci_reg_sequence mode_820x614[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0034 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cb }, + { IMX111_IMAGE_WIDTH, 0x0334 }, { IMX111_IMAGE_HEIGHT, 0x0266 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x05 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x05 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x03 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x00 }, { CCI_REG8(0x30d7), 0x00 }, + { CCI_REG8(0x30d8), 0x00 }, { CCI_REG8(0x30d9), 0x00 }, + { CCI_REG8(0x30de), 0x04 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x7a }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1308x736[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0154 }, { IMX111_VERTICAL_START, 0x0220 }, + { IMX111_HORIZONTAL_END, 0x0b8b }, { IMX111_VERTICAL_END, 0x07df }, + { IMX111_IMAGE_WIDTH, 0x051c }, { IMX111_IMAGE_HEIGHT, 0x02e0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x84 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0xd7 }, { CCI_REG8(0x304d), 0x01 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x48 }, { CCI_REG8(0x309c), 0x12 }, + { CCI_REG8(0x309e), 0x04 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x0a }, { CCI_REG8(0x30aa), 0x01 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x04 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x42 }, + { CCI_REG8(0x315d), 0x41 }, { CCI_REG8(0x316e), 0x43 }, + { CCI_REG8(0x316f), 0x42 }, { CCI_REG8(0x3318), 0x62 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1640x924[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b }, + { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x039c }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1640x1232[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf }, + { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x04d0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 }, + { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_1952x1098[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0016 }, { IMX111_VERTICAL_START, 0x016e }, + { IMX111_HORIZONTAL_END, 0x0ccb }, { IMX111_VERTICAL_END, 0x0893 }, + { IMX111_IMAGE_WIDTH, 0x07a0 }, { IMX111_IMAGE_HEIGHT, 0x044a }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x00 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x91 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x10 }, { CCI_REG8(0x3073), 0xa0 }, + { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 }, + { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x0a }, { CCI_REG8(0x307a), 0x0a }, + { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 }, + { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 }, + { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 }, + { CCI_REG8(0x30d5), 0x20 }, { CCI_REG8(0x30d6), 0x85 }, + { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 }, + { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 }, + { CCI_REG8(0x30df), 0x21 }, { CCI_REG8(0x3102), 0x08 }, + { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e }, + { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 }, + { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 }, + { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 }, + { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 }, + { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 }, + { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xA0 }, +}; + +static const struct cci_reg_sequence mode_2100x1200[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0256 }, { IMX111_VERTICAL_START, 0x02a8 }, + { IMX111_HORIZONTAL_END, 0x0a89 }, { IMX111_VERTICAL_END, 0x0757 }, + { IMX111_IMAGE_WIDTH, 0x0834 }, { IMX111_IMAGE_HEIGHT, 0x04b0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 }, + { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 }, + { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 }, + { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c }, + { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d }, + { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x62 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x1098[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x01f6 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x080b }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x044a }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 }, + { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x93 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0xe0 }, + { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 }, + { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x12 }, + { CCI_REG8(0x3079), 0x2a }, { CCI_REG8(0x307a), 0x0a }, + { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 }, + { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 }, + { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 }, + { CCI_REG8(0x30d5), 0x00 }, { CCI_REG8(0x30d6), 0x85 }, + { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 }, + { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 }, + { CCI_REG8(0x30df), 0x20 }, { CCI_REG8(0x3102), 0x08 }, + { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e }, + { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 }, + { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 }, + { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 }, + { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 }, + { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 }, + { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x1848[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x0738 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 }, + { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 }, + { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 }, + { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 }, + { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d }, + { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e }, + { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct cci_reg_sequence mode_3280x2464[] =3D { + { IMX111_GROUP_WRITE, 1 }, + { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 }, + { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf }, + { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x09a0 }, + { IMX111_GROUP_WRITE, 0 }, + { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 }, + { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 }, + { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 }, + { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 }, + { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 }, + { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 }, + { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 }, + { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 }, + { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 }, + { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 }, + { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 }, + { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 }, + { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 }, + { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a }, + { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 }, + { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 }, + { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 }, + { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 }, + { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 }, + { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 }, + { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d }, + { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e }, + { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 }, + { CCI_REG8(0x3348), 0xe0 }, +}; + +static const struct imx111_mode imx111_modes[] =3D { + [IMX111_MODE_3280x2464] =3D { + .width =3D 3280, + .height =3D 2464, + .vtl_def =3D 2490, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_3280x2464, + .num_of_regs =3D ARRAY_SIZE(mode_3280x2464), + }, + }, + [IMX111_MODE_3280x1848] =3D { + .width =3D 3280, + .height =3D 1848, + .vtl_def =3D 1874, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_3280x1848, + .num_of_regs =3D ARRAY_SIZE(mode_3280x1848), + }, + }, + [IMX111_MODE_3280x1098] =3D { + .width =3D 3280, + .height =3D 1098, + .vtl_def =3D 1130, + .htl_def =3D 3500, + .reg_list =3D { + .regs =3D mode_3280x1098, + .num_of_regs =3D ARRAY_SIZE(mode_3280x1098), + }, + }, + [IMX111_MODE_2100x1200] =3D { + .width =3D 2100, + .height =3D 1200, + .vtl_def =3D 1260, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_2100x1200, + .num_of_regs =3D ARRAY_SIZE(mode_2100x1200), + }, + }, + [IMX111_MODE_1952x1098] =3D { + .width =3D 1952, + .height =3D 1098, + .vtl_def =3D 1884, + .htl_def =3D 3500, + .reg_list =3D { + .regs =3D mode_1952x1098, + .num_of_regs =3D ARRAY_SIZE(mode_1952x1098), + }, + }, + [IMX111_MODE_1920x1080] =3D { + .width =3D 1920, + .height =3D 1080, + .vtl_def =3D 1884, + .htl_def =3D 3500, + .reg_list =3D { + .regs =3D mode_1952x1098, + .num_of_regs =3D ARRAY_SIZE(mode_1952x1098), + }, + }, + [IMX111_MODE_1640x1232] =3D { + .width =3D 1640, + .height =3D 1232, + .vtl_def =3D 1254, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_1640x1232, + .num_of_regs =3D ARRAY_SIZE(mode_1640x1232), + }, + }, + [IMX111_MODE_1440x1080] =3D { + .width =3D 1440, + .height =3D 1080, + .vtl_def =3D 1254, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_1640x1232, + .num_of_regs =3D ARRAY_SIZE(mode_1640x1232), + }, + }, + [IMX111_MODE_1640x924] =3D { + .width =3D 1640, + .height =3D 924, + .vtl_def =3D 946, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_1640x924, + .num_of_regs =3D ARRAY_SIZE(mode_1640x924), + }, + }, + [IMX111_MODE_1308x736] =3D { + .width =3D 1308, + .height =3D 736, + .vtl_def =3D 2369, + .htl_def =3D 1896, + .reg_list =3D { + .regs =3D mode_1308x736, + .num_of_regs =3D ARRAY_SIZE(mode_1308x736), + }, + }, + [IMX111_MODE_1280x720] =3D { + .width =3D 1280, + .height =3D 720, + .vtl_def =3D 2369, + .htl_def =3D 1896, + .reg_list =3D { + .regs =3D mode_1308x736, + .num_of_regs =3D ARRAY_SIZE(mode_1308x736), + }, + }, + [IMX111_MODE_820x614] =3D { + .width =3D 820, + .height =3D 614, + .vtl_def =3D 1260, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_820x614, + .num_of_regs =3D ARRAY_SIZE(mode_820x614), + }, + }, + [IMX111_MODE_640x480] =3D { + .width =3D 640, + .height =3D 480, + .vtl_def =3D 1260, + .htl_def =3D 3536, + .reg_list =3D { + .regs =3D mode_820x614, + .num_of_regs =3D ARRAY_SIZE(mode_820x614), + }, + }, +}; + +static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct imx111, sd); +} + +static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl) +{ + return container_of_const(ctrl->handler, struct imx111, hdl); +} + +static u8 to_settle_delay(u64 extclk_rate) +{ + u64 extclk_mhz =3D div_u64(extclk_rate, MEGA); + + return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63, + 64); +} + +static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool te= st) +{ + u32 i; + + for (i =3D 0; i < ARRAY_SIZE(imx111_mbus_formats); i++) + if (imx111_mbus_formats[i] =3D=3D code) + break; + + if (i >=3D ARRAY_SIZE(imx111_mbus_formats)) + i =3D 0; + + if (test) + return imx111_mbus_formats[i]; + + i =3D (i & ~3) | (sensor->vflip->val ? 2 : 0) | + (sensor->hflip->val ? 1 : 0); + + return imx111_mbus_formats[i]; +} + +static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format) +{ + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return 8; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static int imx111_update_digital_gain(struct imx111 *sensor, u32 val) +{ + int ret =3D 0; + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret); + cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret); + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + + return ret; +} + +static int imx111_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx111 *sensor =3D ctrl_to_imx111(ctrl); + struct device *dev =3D regmap_get_device(sensor->regmap); + int ret =3D 0; + + if (ctrl->id =3D=3D V4L2_CID_VBLANK) { + s64 max =3D sensor->cur_mode->height + ctrl->val - + IMX111_INTEGRATION_TIME_OFFSET; + + ret =3D __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + max, sensor->exposure->step, + max); + if (ret) + return ret; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, + &ret); + break; + case V4L2_CID_DIGITAL_GAIN: + ret =3D imx111_update_digital_gain(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_HBLANK: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_HORIZONTAL_TOTAL_LENGTH, + sensor->cur_mode->width + ctrl->val, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_VBLANK: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_VERTICAL_TOTAL_LENGTH, + sensor->cur_mode->height + ctrl->val, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION, + sensor->hflip->val | sensor->vflip->val << 1, &ret); + break; + case V4L2_CID_TEST_PATTERN: + cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, + &ret); + break; + case V4L2_CID_TEST_PATTERN_RED: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, + &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + break; + default: + ret =3D -EINVAL; + } + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx111_ctrl_ops =3D { + .s_ctrl =3D imx111_set_ctrl, +}; + +static const char * const test_pattern_menu[] =3D { + "Disabled", + "Solid Color Fill", + "Standard Color Bars", + "Fade To Grey Color Bars", + "Pseudorandom data", +}; + +static int imx111_init_controls(struct imx111 *sensor) +{ + const struct v4l2_ctrl_ops *ops =3D &imx111_ctrl_ops; + struct device *dev =3D regmap_get_device(sensor->regmap); + const struct imx111_mode *mode =3D sensor->cur_mode; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *hdl =3D &sensor->hdl; + s64 pixel_rate_min, pixel_rate_max; + int i, ret; + + ret =3D v4l2_fwnode_device_parse(dev, &props); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(hdl, 15); + + pixel_rate_min =3D div_u64(sensor->pixel_clk_raw, + 2 * IMX111_DATA_DEPTH_RAW10); + pixel_rate_max =3D div_u64(sensor->pixel_clk_raw, + 2 * IMX111_DATA_DEPTH_RAW8); + sensor->pixel_rate =3D v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, + pixel_rate_min, pixel_rate_max, + 1, + div_u64(sensor->pixel_clk_raw, + 2 * + sensor->data_depth)); + + sensor->link_freq =3D v4l2_ctrl_new_int_menu(hdl, NULL, + V4L2_CID_LINK_FREQ, 0, 0, + &sensor->default_link_freq); + if (sensor->link_freq) + sensor->link_freq->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX, + IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX, + IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT); + + sensor->hflip =3D v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, + 0); + if (sensor->hflip) + sensor->hflip->flags |=3D V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vflip =3D v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, + 0); + if (sensor->vflip) + sensor->vflip->flags |=3D V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vblank =3D v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + IMX111_VBLANK_MIN, + IMX111_VTL_MAX - mode->height, 1, + mode->vtl_def - mode->height); + sensor->hblank =3D v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + IMX111_HBLANK_MIN, + IMX111_HTL_MAX - mode->width, 1, + mode->htl_def - mode->width); + + /* + * The maximum coarse integration time is the frame length in lines + * minus five. + */ + sensor->exposure =3D v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + IMX111_INTEGRATION_TIME_MIN, + IMX111_PIXEL_ARRAY_HEIGHT - + IMX111_INTEGRATION_TIME_OFFSET, + IMX111_INTEGRATION_TIME_STEP, + IMX111_PIXEL_ARRAY_HEIGHT - + IMX111_INTEGRATION_TIME_OFFSET); + + v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, + test_pattern_menu); + for (i =3D 0; i < 4; i++) { + /* + * The assumption is that + * TEST_PATTERN_GREENR =3D=3D TEST_PATTERN_RED + 1 + * TEST_PATTERN_BLUE =3D=3D TEST_PATTERN_RED + 2 + * TEST_PATTERN_GREENB =3D=3D TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i, + IMX111_TESTP_COLOUR_MIN, + IMX111_TESTP_COLOUR_MAX, + IMX111_TESTP_COLOUR_STEP, + IMX111_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } + + if (hdl->error) + return hdl->error; + + sensor->sd.ctrl_handler =3D hdl; + + return 0; +}; + +static int imx111_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + struct device *dev =3D regmap_get_device(sensor->regmap); + const struct imx111_mode *mode =3D sensor->cur_mode; + int ret; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Apply default values of current mode */ + ret =3D cci_multi_reg_write(sensor->regmap, mode->reg_list.regs, + mode->reg_list.num_of_regs, NULL); + if (ret < 0) { + dev_err(dev, "Failed to initialize the sensor\n"); + goto err_rpm_put; + } + + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, + IMX111_GROUP_WRITE_ON, &ret); + cci_write(sensor->regmap, IMX111_DATA_DEPTH, + sensor->data_depth | sensor->data_depth << 8, &ret); + cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, + IMX111_GROUP_WRITE_ON, 0, &ret); + + if (ret) + goto err_rpm_put; + + ret =3D __v4l2_ctrl_handler_setup(&sensor->hdl); + if (ret) + goto err_rpm_put; + + ret =3D cci_write(sensor->regmap, IMX111_STREAMING_MODE, + IMX111_MODE_STREAMING, NULL); + if (ret) + dev_err(dev, "failed to start stream"); + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(sensor->vflip, true); + __v4l2_ctrl_grab(sensor->hflip, true); + + msleep(30); + + return 0; + +err_rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int imx111_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + struct device *dev =3D regmap_get_device(sensor->regmap); + int ret; + + ret =3D cci_write(sensor->regmap, IMX111_STREAMING_MODE, + IMX111_MODE_STANDBY, NULL); + if (ret) + dev_err(dev, "failed to stop stream\n"); + + __v4l2_ctrl_grab(sensor->vflip, false); + __v4l2_ctrl_grab(sensor->hflip, false); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int imx111_initialize(struct imx111 *sensor) +{ + struct device *dev =3D regmap_get_device(sensor->regmap); + int ret =3D 0; + + /* Configure the PLL. */ + cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1, + sensor->pll->pre_div, &ret); + cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, + sensor->pll->mult, &ret); + cci_write(sensor->regmap, IMX111_POST_DIVIDER, + IMX111_POST_DIVIDER_DIV1, &ret); + cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME, + to_settle_delay(sensor->pll->extclk_rate), &ret); + + cci_multi_reg_write(sensor->regmap, imx111_global_init, + ARRAY_SIZE(imx111_global_init), &ret); + if (ret < 0) { + dev_err(dev, "Failed to initialize the sensor\n"); + return ret; + } + + return 0; +} + +/* -----------------------------------------------------------------------= ----- + * IMX111 Pad Subdev Init and Operations + */ +static int imx111_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + + if (code->index >=3D ARRAY_SIZE(imx111_mbus_formats) / 4) + return -EINVAL; + + code->code =3D imx111_get_format_code(sensor, + imx111_mbus_formats[code->index * + 4], false); + + return 0; +} + +static int imx111_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + u32 code; + + if (fse->index >=3D ARRAY_SIZE(imx111_modes)) + return -EINVAL; + + code =3D imx111_get_format_code(sensor, fse->code, true); + if (fse->code !=3D code) + return -EINVAL; + + fse->min_width =3D imx111_modes[fse->index].width; + fse->max_width =3D fse->min_width; + fse->min_height =3D imx111_modes[fse->index].height; + fse->max_height =3D fse->min_height; + + return 0; +} + +static int imx111_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + struct v4l2_mbus_framefmt *mbus_fmt =3D &format->format; + struct v4l2_mbus_framefmt *fmt; + const struct imx111_mode *mode; + + mode =3D v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes), + width, height, + mbus_fmt->width, mbus_fmt->height); + + fmt =3D v4l2_subdev_state_get_format(state, format->pad); + + fmt->code =3D imx111_get_format_code(sensor, mbus_fmt->code, false); + fmt->width =3D mode->width; + fmt->height =3D mode->height; + fmt->colorspace =3D V4L2_COLORSPACE_RAW; + + *mbus_fmt =3D *fmt; + + if (format->which =3D=3D V4L2_SUBDEV_FORMAT_ACTIVE) { + int ret; + + sensor->cur_mode =3D mode; + sensor->data_depth =3D imx111_get_format_bpp(fmt); + + ret =3D __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, + div_u64(sensor->pixel_clk_raw, + 2 * + sensor->data_depth)); + if (ret) + return ret; + + ret =3D __v4l2_ctrl_modify_range(sensor->vblank, + IMX111_VBLANK_MIN, + IMX111_VTL_MAX - mode->height, + 1, + mode->vtl_def - mode->height); + if (ret) + return ret; + + ret =3D __v4l2_ctrl_s_ctrl(sensor->vblank, mode->vtl_def - + mode->height); + if (ret) + return ret; + + ret =3D __v4l2_ctrl_modify_range(sensor->hblank, + IMX111_HBLANK_MIN, + IMX111_HTL_MAX - mode->width, + 1, + mode->htl_def - mode->width); + if (ret) + return ret; + + ret =3D __v4l2_ctrl_s_ctrl(sensor->hblank, mode->htl_def - + mode->width); + if (ret) + return ret; + } + + return 0; +} + +static int imx111_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct imx111 *sensor =3D sd_to_imx111(sd); + const struct imx111_mode *mode =3D sensor->cur_mode; + struct v4l2_mbus_framefmt *fmt; + + fmt =3D v4l2_subdev_state_get_format(sd_state, 0); + + fmt->code =3D MEDIA_BUS_FMT_SGBRG10_1X10; + fmt->width =3D mode->width; + fmt->height =3D mode->height; + fmt->field =3D V4L2_FIELD_NONE; + fmt->colorspace =3D V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc =3D V4L2_YCBCR_ENC_601; + fmt->quantization =3D V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func =3D V4L2_XFER_FUNC_NONE; + + return 0; +} + +static const struct v4l2_subdev_video_ops imx111_video_ops =3D { + .s_stream =3D v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx111_pad_ops =3D { + .enum_mbus_code =3D imx111_enum_mbus_code, + .enum_frame_size =3D imx111_enum_frame_size, + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D imx111_set_format, + .enable_streams =3D imx111_enable_streams, + .disable_streams =3D imx111_disable_streams, +}; + +static const struct v4l2_subdev_ops imx111_subdev_ops =3D { + .video =3D &imx111_video_ops, + .pad =3D &imx111_pad_ops, +}; + +static const struct media_entity_operations imx111_subdev_entity_ops =3D { + .link_validate =3D v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops imx111_internal_ops =3D { + .init_state =3D imx111_init_state, +}; + +static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *cl= ient) +{ + struct device *dev =3D &client->dev; + struct v4l2_subdev *sd =3D &sensor->sd; + struct media_pad *pad =3D &sensor->pad; + struct v4l2_ctrl_handler *hdl =3D &sensor->hdl; + int ret; + + /* Initialize the subdev. */ + v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops); + + sd->flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops =3D &imx111_internal_ops; + + /* Initialize the media entity. */ + sd->entity.function =3D MEDIA_ENT_F_CAM_SENSOR; + sd->entity.ops =3D &imx111_subdev_entity_ops; + pad->flags =3D MEDIA_PAD_FL_SOURCE; + + ret =3D media_entity_pads_init(&sd->entity, 1, pad); + if (ret < 0) { + dev_err(dev, "failed to init entity pads: %d", ret); + return ret; + } + + /* Initialize the control handler. */ + ret =3D imx111_init_controls(sensor); + if (ret) + goto error; + + return 0; +error: + v4l2_ctrl_handler_free(hdl); + media_entity_cleanup(&sd->entity); + return ret; +}; + +/* -----------------------------------------------------------------------= ----- + * Power Management + */ + +static int imx111_power_on(struct imx111 *sensor) +{ + int ret; + + if (sensor->reset) + gpiod_set_value(sensor->reset, 1); + + ret =3D regulator_bulk_enable(ARRAY_SIZE(imx111_supplies), + sensor->supplies); + if (ret < 0) + return ret; + + usleep_range(500, 600); + + if (sensor->reset) + gpiod_set_value(sensor->reset, 0); + + usleep_range(200, 250); + + ret =3D clk_prepare_enable(sensor->extclk); + if (ret < 0) + goto error_regulator; + + usleep_range(200, 250); + + return 0; + +error_regulator: + regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies); + return ret; +} + +static void imx111_power_off(struct imx111 *sensor) +{ + if (sensor->reset) + gpiod_set_value(sensor->reset, 1); + usleep_range(1000, 2000); + + clk_disable_unprepare(sensor->extclk); + regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies); +} + +static int __maybe_unused imx111_pm_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd =3D dev_get_drvdata(dev); + struct imx111 *sensor =3D sd_to_imx111(sd); + int ret; + + ret =3D imx111_power_on(sensor); + if (ret) + return ret; + + ret =3D imx111_initialize(sensor); + if (ret) { + imx111_power_off(sensor); + return ret; + } + + return 0; +} + +static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd =3D dev_get_drvdata(dev); + struct imx111 *sensor =3D sd_to_imx111(sd); + + imx111_power_off(sensor); + + return 0; +} + +static const struct dev_pm_ops imx111_pm_ops =3D { + SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend, + imx111_pm_runtime_resume, NULL) +}; + +/* -----------------------------------------------------------------------= ----- + * Probe & Remove + */ + +static int imx111_identify_module(struct imx111 *sensor) +{ + struct device *dev =3D regmap_get_device(sensor->regmap); + u64 value, revision, manufacturer; + int ret =3D 0; + + ret =3D cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL); + if (ret) + return ret; + + if (value !=3D IMX111_CHIP_ID) { + dev_err(dev, "chip id mismatch: %x!=3D%04llx", IMX111_CHIP_ID, + value); + return -ENXIO; + } + + cci_read(sensor->regmap, IMX111_REVISION, &revision, &ret); + cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, &ret); + + dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n", + value, revision, manufacturer); + + return ret; +} + +static int imx111_clk_init(struct imx111 *sensor) +{ + struct device *dev =3D regmap_get_device(sensor->regmap); + u32 ndata_lanes =3D sensor->bus_cfg.bus.mipi_csi2.num_data_lanes; + u64 extclk_rate, system_clk; + unsigned int i; + + extclk_rate =3D clk_get_rate(sensor->extclk); + if (!extclk_rate) + return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n"); + + for (i =3D 0; i < ARRAY_SIZE(imx111_pll); i++) { + if (clk_get_rate(sensor->extclk) =3D=3D + imx111_pll[i].extclk_rate) { + sensor->pll =3D &imx111_pll[i]; + break; + } + } + if (!sensor->pll) + return dev_err_probe(dev, -EINVAL, + "Unsupported EXTCLK rate %llu\n", + extclk_rate); + + system_clk =3D div_u64(extclk_rate, sensor->pll->pre_div) * + sensor->pll->mult; + + /* + * Pixel clock or Logic clock is used for internal image processing is + * generated by dividing into 1/10 or 1/8 frequency according to the + * word length of the CSI2 interface. This clock is designating the + * pixel rate and used as the base of integration time, frame rate etc. + */ + sensor->pixel_clk_raw =3D system_clk * ndata_lanes; + + /* + * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR + * over n lanes for RAW10 default format. + */ + sensor->default_link_freq =3D div_u64(sensor->pixel_clk_raw * 8, + 2 * IMX111_DATA_DEPTH_RAW10); + + if (sensor->bus_cfg.nr_of_link_frequencies !=3D 1 || + sensor->bus_cfg.link_frequencies[0] !=3D sensor->default_link_freq) + return dev_err_probe(dev, -EINVAL, + "Invalid link-frequency, expected %llu\n", + sensor->default_link_freq); + + return 0; +} + +static int imx111_parse_dt(struct imx111 *sensor) +{ + struct device *dev =3D regmap_get_device(sensor->regmap); + struct fwnode_handle *fwnode =3D dev_fwnode(dev); + struct fwnode_handle *ep; + int ret; + + ep =3D fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(dev, "No endpoint found\n"); + return -EINVAL; + } + + ret =3D v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg); + fwnode_handle_put(ep); + if (ret < 0) { + dev_err(dev, "Failed to parse endpoint\n"); + goto error; + } + + sensor->bus_cfg.bus_type =3D V4L2_MBUS_CSI2_DPHY; + + /* Check the number of MIPI CSI2 data lanes */ + if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) { + dev_err(dev, "number of lanes is more than 2\n"); + ret =3D -EINVAL; + goto error; + } + + return 0; + +error: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static int imx111_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct imx111 *sensor; + int ret; + + sensor =3D devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->regmap =3D devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to allocate register map\n"); + + sensor->extclk =3D devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(sensor->extclk)) + return dev_err_probe(dev, PTR_ERR(sensor->extclk), + "Failed to get clock\n"); + + sensor->reset =3D devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sensor->reset)) + return dev_err_probe(dev, PTR_ERR(sensor->reset), + "Failed to get reset GPIO\n"); + + ret =3D devm_regulator_bulk_get_const(dev, ARRAY_SIZE(imx111_supplies), + imx111_supplies, + &sensor->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret =3D imx111_parse_dt(sensor); + if (ret < 0) + return ret; + + ret =3D imx111_clk_init(sensor); + if (ret < 0) + goto error_ep_free; + + ret =3D imx111_power_on(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not power on the device\n"); + goto error_ep_free; + } + + ret =3D imx111_identify_module(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not identify module\n"); + goto error_power_off; + } + + sensor->cur_mode =3D &imx111_modes[IMX111_MODE_3280x2464]; + sensor->data_depth =3D IMX111_DATA_DEPTH_RAW10; + + ret =3D imx111_initialize(sensor); + if (ret < 0) + goto error_power_off; + + ret =3D imx111_init_subdev(sensor, client); + if (ret < 0) { + dev_err(dev, "failed to init controls: %d", ret); + goto error_v4l2_ctrl_handler_free; + } + + ret =3D v4l2_subdev_init_finalize(&sensor->sd); + if (ret) + goto error_v4l2_ctrl_handler_free; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret =3D v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret < 0) { + dev_err(dev, "failed to register V4L2 subdev: %d", ret); + goto error_pm; + } + + pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + return 0; + +error_pm: + v4l2_subdev_cleanup(&sensor->sd); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&sensor->hdl); + media_entity_cleanup(&sensor->sd.entity); + +error_power_off: + imx111_power_off(sensor); + +error_ep_free: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + return ret; +} + +static void imx111_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx111 *sensor =3D sd_to_imx111(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->hdl); + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + imx111_power_off(sensor); + pm_runtime_set_suspended(&client->dev); + } +} + +static const struct of_device_id imx111_of_match[] =3D { + { .compatible =3D "sony,imx111" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx111_of_match); + +static struct i2c_driver imx111_i2c_driver =3D { + .driver =3D { + .name =3D "imx111", + .of_match_table =3D imx111_of_match, + .pm =3D &imx111_pm_ops, + }, + .probe =3D imx111_probe, + .remove =3D imx111_remove, +}; +module_i2c_driver(imx111_i2c_driver); + +MODULE_AUTHOR("Svyatoslav Ryhel "); +MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0