From nobody Fri Jul 24 21:30:22 2026 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) (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 935C23546FD; Fri, 24 Jul 2026 15:37:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.167.242.64 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784907435; cv=none; b=eD9sIJTe6bNxGw7fKLMUnYkI3B0TR/2K5aEka69fayyGcAa0lddp90X1mEM9odicr9affDjDJcc4uz5giIEEapYUAL1iYZpy4lcRNI8YlzZyLac2KHxqx+JtCyQDrUMOYz9YWSHORBpM4tX9NNtv5cMl8WWWzcVGjRtfc6xUgJk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784907435; c=relaxed/simple; bh=JOL6+MUTtIG/TQyeu1UTGqTL8FXxVfQQYy8mRlBHY6s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jiGl9Pdc86m3M7hf8uAIXT8eQI4+N+OAuKOaHPYLrRhXspUVnhhNMnr5FxB08NIntZ8HoIvOAUjOWvKUip7yB2Ed1LepZslasJ3LEsWPpOKRJ0W3Wq6z86/+mt4dHNq74HbKjMa9Tbugrr7Y6+ZfxmuOKV+935aiXbNmoh4Hb8c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com; spf=pass smtp.mailfrom=ideasonboard.com; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b=dUg0fW+j; arc=none smtp.client-ip=213.167.242.64 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dUg0fW+j" Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 414FA73B; Fri, 24 Jul 2026 17:36:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1784907369; bh=JOL6+MUTtIG/TQyeu1UTGqTL8FXxVfQQYy8mRlBHY6s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dUg0fW+jmlCMIeafMxRNeIwa607RV/7MJkj2rq1QTqrLZaDePuC9ot9yYpCCrGHkM za4mIJK+HvbB1GQRU3eB/Alfb2wFzc09FCfGbdSX+p5i1aKKzqzgJqYFHTXXSnya/8 LUAajLfKhmH2KiXe745VXVGpVyaQUbIYfg/JXfcU= From: Jacopo Mondi Date: Fri, 24 Jul 2026 17:37:00 +0200 Subject: [PATCH v8 1/2] dt-bindings: media: i2c: Add mira220 image sensor Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260724-mira220-v8-1-d1899ab64709@ideasonboard.com> References: <20260724-mira220-v8-0-d1899ab64709@ideasonboard.com> In-Reply-To: <20260724-mira220-v8-0-d1899ab64709@ideasonboard.com> To: Philippe Baetens , Mauro Carvalho Chehab , Rob Herring , Sakari Ailus , Krzysztof Kozlowski , Conor Dooley Cc: Kieran Bingham , Jai Luthra , linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Jacopo Mondi , Conor Dooley X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4297; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=7qVkN51rHFIciMJvA1Z8D53Z8lggwDO8fAruqf/OdbQ=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqY4alrUK3EAyCEjjFFaYqEQYZqYXU92h/z/ocw 7FY7Zpj6PGJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCamOGpQAKCRByNAaPFqFW PCfYEACIC3ds+XLoKfEvol6LG8hNDfCHxlF+cvdBAROERwMCsdoIXs5+q9hvBKH0N4EvKBwZJcU Vb5XNDBVcXZPBwmx/yaJNmvdH2fjR8sk/sWGY2H4e3q9u6DWjDACz1Jn+J/k8j8gLoQTCYHmKkt DnYc+sEkgj4cJiegxRMyYX3D75KsxXYnnSZ6t7DrGiVkf27T+iCwoPa68wplvI7Q2u/gXfikpEN iNMeCJTQhD+1rw41ioxoioQXrr8XUxFCx+DY9ks2q3++vdtni7dh4FL+/U9LmMi6YtiU8yjo4x8 8ebSKC4jyZcM4gfWt9xp5XJNTNf/0PWWAq1YHjenOZHMmFGdfpWenzEC7FeO02gi8pUpGd7gkHW I2PeMyGXVWkYUSP/cXjKVLK84LDzSu/uh/xfnWd3+Kbv8PWyVN2xY1jkBCc++miI9ElcUYDIEK4 pqiDSjyQBX9ZaTxUIQDknKFx+aeDs3ka31uojqrfbZH58kQFDmZg+bJY2aGoIu+2xUa7sv6GmnP YZu+HBsI63NHamEKBqhobztclBKWVAUb3YGqBLXYuBFmzj2pHSFG3zkvN8/5gzXUczS/1SShTHU MQWt3MNZnK6zm7QwEygsjeq91SauPnAqj3OFdxMGDTvPuCUq3EcqZ4xv2LkB2xW+UVh/KdSOiYB XRUDB1TEkfI/RNw== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B From: Philippe Baetens Add bindings for the AMSOSRAM Mira220 image sensor. Acked-by: Conor Dooley Signed-off-by: Philippe Baetens Signed-off-by: Jacopo Mondi --- v7->v8: - Add Conor's tag v5->v6: - sashiko: use 'unevaluatedProperties: false' at the top level to allow properties from video-interface-devices.yaml v4->v5: - reflow description to 80 cols and drop |- - remove 'reg' description - remove 'anyOf' from data-lanes - remove 'link-frequencies' from required properties - remove 'clock-continuous: true' as we already allow all properties from 'video-interfaces.yaml' - remove tabs from the example --- .../devicetree/bindings/media/i2c/ams,mira220.yaml | 101 +++++++++++++++++= ++++ MAINTAINERS | 7 ++ 2 files changed, 108 insertions(+) diff --git a/Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml b= /Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml new file mode 100644 index 000000000000..d113f7f81ffb --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ams,mira220.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMS 2.2 MP NIR enhanced global shutter image sensor + +maintainers: + - Philippe Baetens + +description: + 2.2 MP NIR enhanced global shutter image sensor designed for 2D and 3D + consumer and industrial machine vision applications. Due to its small si= ze, + configurability and high sensitivity both in visual as well as NIR, the + Mira220 is well suited for 2D and 3D applications, which include Active = Stereo + Vision, Structured Light Vision for Robotics and AR/VR. + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: ams,mira220 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + vdig-supply: + description: + Digital I/O voltage supply, 1.35 volts + + vana-supply: + description: + Analog1 voltage supply, 2.8 volts + + vddl-supply: + description: + Digital core voltage supply, 1.8 volts + + reset-gpios: + description: Sensor reset (XCLR) GPIO + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: + Video output port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + items: + - const: 1 + - const: 2 + + required: + - data-lanes + +required: + - compatible + - reg + - clocks + - vdig-supply + - vana-supply + - vddl-supply + - port + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + mira220: camera-sensor@54 { + compatible =3D "ams,mira220"; + reg =3D <0x54>; + clocks =3D <&cam1_clk>; + vana-supply =3D <&cam1_reg>; + vdig-supply =3D <&cam_dummy_reg>; + vddl-supply =3D <&cam_dummy_reg>; + port { + mira220_ep: endpoint { + clock-lanes =3D <0>; + data-lanes =3D <1 2>; + link-frequencies =3D /bits/ 64 <750000000>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index febe918d6316..55ac4ec433a6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1430,6 +1430,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/ams,as73211.yaml F: drivers/iio/light/as73211.c =20 +AMS MIRA220 DRIVER +M: Philippe Baetens +M: Jacopo Mondi +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml + AMT (Automatic Multicast Tunneling) M: Taehee Yoo L: netdev@vger.kernel.org --=20 2.54.0 From nobody Fri Jul 24 21:30:22 2026 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) (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 42AB7370D71; Fri, 24 Jul 2026 15:37:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.167.242.64 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784907443; cv=none; b=PO7BAuW8g+X7QllGLffNpKlWBzC8rt6kbk4sEWyjC5TL4lvXDoD+BeP4l87hrr23jCQiLHx9xUlpVbWrqxmn2w6v39++te1jv3kCANqn20RgkkDFQJRkxTTnQqPirQh5gNbUAaWvgHA0WzqT5s5HrhroBJg1endmz+NPVR6nqG8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784907443; c=relaxed/simple; bh=Kxvg8aljUEoExrGcuSGYHYZlk2URlgxpbnDNNXxb0kI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ey6B0bIK4SZtMA6r9bHywp9Zww+F+klvrV7w5JZZdWDuiK+/r1fIDhPIBvegYG5jKD8O/yHCt8LnJnI9nzFtaJXAR0ILKM1kWGYD/k7omUIA8M2Q5NkJ40Z/x3IJ5LdR3jkc4ooeqovG4WFJi7F/PCgUkvVQUAOfgKoF3CbgCz0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com; spf=pass smtp.mailfrom=ideasonboard.com; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b=j1bqxI3V; arc=none smtp.client-ip=213.167.242.64 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="j1bqxI3V" Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CEFF49A4; Fri, 24 Jul 2026 17:36:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1784907370; bh=Kxvg8aljUEoExrGcuSGYHYZlk2URlgxpbnDNNXxb0kI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=j1bqxI3V6bAtX5AhvWormC5ExzFUEbafSR3pw1cUjciuInRE8Ee5LU5Qriqk4zodV ORMTRiL6L0Gh2XLqe6n+jWPq1CgzFyFeLSO2rTGjieKtPAxp3wmx9Bd3gJwcit1zrQ +y0YatzO6nlkox6+2BsJjzkdFQuq2x0UslKjb58E= From: Jacopo Mondi Date: Fri, 24 Jul 2026 17:37:01 +0200 Subject: [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260724-mira220-v8-2-d1899ab64709@ideasonboard.com> References: <20260724-mira220-v8-0-d1899ab64709@ideasonboard.com> In-Reply-To: <20260724-mira220-v8-0-d1899ab64709@ideasonboard.com> To: Philippe Baetens , Mauro Carvalho Chehab , Rob Herring , Sakari Ailus , Krzysztof Kozlowski , Conor Dooley Cc: Kieran Bingham , Jai Luthra , linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Jacopo Mondi X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=68845; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=ELNBZIGaJBOianU0p+UdVjYosfv1FGMGYd6A+fhHeAI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqY4alCOEkboedUCzkjx7uH9rWdsYoDzKEJd7G8 Tqq5MpyVbKJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCamOGpQAKCRByNAaPFqFW PPb7D/0dUrtXLzuJZhXiAXHmc6wt/gO2d1bo9LIHPqr8LIIu4sv62hclsyvnxkkVRJo6OAxXSvm zu37J8XQm6hc59G48vHyhvF8tM0emuDXI1QUmMrSF9iznx8EEuEzVfCiKiPhFi5tn1taNSQ6UDG 5goUbCBhtuFEPNHmWG2aWnMDWPZDvl7fu6O6oWzmSEQVDmmG3m8r76MWVh8X2bM1arjPZBFeSut jVqXtxvP4jlOwY4jQlzruYkH/1SDITK+x/HEi+FEXCA16qdEYYa1sPAlb9NV11mQ/J0Plhcynxt XVihCOyw5hm1HnyUD+V0zqMRJENM9OjMcvqiq2PsOJj3yr/jXR9pO98YGfWHB4lb2hrFewdGKow IpSplYQrH/Yx+i7341CyCBq7lCKVR+w88TzVrOGE118+iPxp9CpgR8yXW/Mg7EMkq0umwMM0ROB cLTs1cU4jEz+Jyc/SxUg8VF/sObVWsqWk8+nEOkHNQBu/oCzWfGsZTNeu2v8JNH15srhqqR6H6+ rmGvuKcW1CRzICMV6uZH4IS0uNNKuCyRczPzSfLQtVE8oCPkhtEYhj3NW8grMu7zmJHtZeU3ypW dApZADBvruWGpTXnhkBiFvJWVLGn1TECvdwgy2f+z8EDR9rvWC8nZQ76A73pl4AQs+hxYx2chnq UfPTYK23rD1Enug== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B From: Philippe Baetens Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image sensor. Mira220 is a global shutter image sensor with a resolution of 1600x1400 pixels. The driver implements support for mono and RGB 12, 10 and 8 bits formats. The output data-rate per lane is 1500Mbit/s, with a maximum frame rate up to 90 fps. Signed-off-by: Philippe Baetens Signed-off-by: Jacopo Mondi Reviewed-by: Jai Luthra --- v7->v8: - Adjust KConfig entry to only depend on GPIOLIB and select CCI - Register V4L2_CID_LINK_FREQ and validate the dts link-frequencies property - Refuse the clock-continuous dts property - Drop MIRA220_NUM_SUPPLIES and use ARRAY_SIZE() - Rework flip index computation through a dedicated function - Re-sort mira220_mbus_color_formats[] to avoid inverting hflip value when adjusting format to flips - Address Jai's review comments - Re-indent macros with tabs - Use container_of_const - Use lowercase for hex values - Remove left-over mira220_mode * - Use HSTART_MASK when programming register - Use cci_write(..., &ret) to simplify register write sequences - Drop outdate comment - Do not * 1 sizes to initialize crop rectangle - Remove support for events - Use devm_v4l2_sensor_clk_get() to support ACPI systems v6->v7: - Address Sashiko review comments: - Return error code from mira220_power_on() - Use if (!pm_runtime_get_if_in_use(&client->dev)) in set_ctrl to support !PM - Power OTP off in case of errors - Use pm_ptr() when initializing the pm ops v5->v6: - Address Sashiko review comments: - Clamp max exposure not to overlow the 16 bits register - Update pad format on flip - Check otp_read() return value before accessing 'id' - Add v4l2_subdev_cleanup() to remove() - Do not access vblank in init_controls - Enable pm_runtime autosuspend last in probe - Scale hblank according to lane number to match row_length v4->v5: - Address checkpatch warnings and other minor style fixes (c++ comments 80-cols etc) - Restore MEDIA_BUS_FMT_Y12_1X12 format - Run-time identify mono vs rgb variants - Remove debug messages - Remove delays not documented in datasheet - Adjust min/max blankings to datasheet values - Do not update limits of read-only controls with a single values on set_fmt - Use pm_runtime autosuspend delay - Remove fixed modes and dynamically program: - CSI-2 bus config (lanes number and clock mode) - frame windowing - image format and data-type - Remove programming of secondary page for fast switiching (not used yet) - Add support for reset_gpio in driver - Use control's mutex for state locking - Use format from subdev state and remove format from driver structure - Rework OTP programming routines to shorten error handling - Use enable/disable_streams() in place of set_stream() - Add fwnode parsing for data lanes configuration --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/mira220.c | 2080 +++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 2094 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55ac4ec433a6..7c4f743ae026 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1436,6 +1436,7 @@ M: Jacopo Mondi L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml +F: drivers/media/i2c/mira220.c =20 AMT (Automatic Multicast Tunneling) M: Taehee Yoo diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5c52007f9cbe..650d5c86baa4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -313,6 +313,18 @@ config VIDEO_IMX678 config VIDEO_MAX9271_LIB tristate =20 +config VIDEO_MIRA220 + tristate "ams MIRA220 sensor support" + depends on I2C && VIDEO_DEV + depends on GPIOLIB + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the ams + MIRA220 camera. + + To compile this driver as a module, choose M here: the + module will be called mira220. + config VIDEO_MT9M001 tristate "mt9m001 support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index d04bd5724552..6ecfe6c65af4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_VIDEO_MAX9271_LIB) +=3D max9271.o obj-$(CONFIG_VIDEO_MAX9286) +=3D max9286.o obj-$(CONFIG_VIDEO_MAX96714) +=3D max96714.o obj-$(CONFIG_VIDEO_MAX96717) +=3D max96717.o +obj-$(CONFIG_VIDEO_MIRA220) +=3D mira220.o obj-$(CONFIG_VIDEO_ML86V7667) +=3D ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) +=3D msp3400.o obj-$(CONFIG_VIDEO_MT9M001) +=3D mt9m001.o diff --git a/drivers/media/i2c/mira220.c b/drivers/media/i2c/mira220.c new file mode 100644 index 000000000000..8b9148efc581 --- /dev/null +++ b/drivers/media/i2c/mira220.c @@ -0,0 +1,2080 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA220 cameras. + * + * Mira220 comes in monochrome, RGB and IR-RGB variants. The driver suppor= ts + * the mono and RGB variants. + * + * Copyright (C) 2026, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Active pixel array is 1600 (H) x 1400 (V) pixels. + * Physical resolution including buffer pixels: 1642 (H) x 1464 (V) pixels. + */ +#define MIRA220_NATIVE_WIDTH 1642U +#define MIRA220_NATIVE_HEIGHT 1464U +#define MIRA220_PIXEL_ARRAY_LEFT 21U +#define MIRA220_PIXEL_ARRAY_TOP 32U +#define MIRA220_PIXEL_ARRAY_WIDTH 1600U +#define MIRA220_PIXEL_ARRAY_HEIGHT 1400U + +/* Mira220 does not support analog gain. */ +#define MIRA220_ANALOG_GAIN_MIN 1 +#define MIRA220_ANALOG_GAIN_MAX 1 +#define MIRA220_ANALOG_GAIN_STEP 1 +#define MIRA220_ANALOG_GAIN_DEFAULT MIRA220_ANALOG_GAIN_MIN + +/* Bit order */ +#define MIRA220_BIT_ORDER_REG CCI_REG8(0x2063) +#define MIRA220_BIT_ORDER_NORMAL 0x00 +#define MIRA220_BIT_ORDER_REVERSED 0x01 + +/* Bit depth */ +#define MIRA220_BIT_DEPTH_REG CCI_REG8(0x209e) +#define MIRA220_BIT_DEPTH_12_BIT 0x02 +#define MIRA220_BIT_DEPTH_10_BIT 0x04 +#define MIRA220_BIT_DEPTH_8_BIT 0x06 +#define MIRA220_CSI_DATA_TYPE_REG CCI_REG8(0x208d) +#define MIRA220_CSI_DATA_TYPE_12_BIT 0x04 +#define MIRA220_CSI_DATA_TYPE_10_BIT 0x02 +#define MIRA220_CSI_DATA_TYPE_8_BIT 0x01 + +/* Imager state master/slave registers */ +#define MIRA220_IMAGER_STATE_REG CCI_REG8(0x1003) +#define MIRA220_IMAGER_STATE_STOP_AT_ROW 0x02 +#define MIRA220_IMAGER_STATE_STOP_AT_FRAME 0x04 +#define MIRA220_IMAGER_STATE_MASTER_CONTROL 0x10 +#define MIRA220_IMAGER_STATE_SLAVE_CONTROL 0x08 + +/* Start image acquisition */ +#define MIRA220_IMAGER_RUN_REG CCI_REG8(0x10f0) +#define MIRA220_IMAGER_RUN_START 0x01 +#define MIRA220_IMAGER_RUN_STOP 0x00 + +/* Continuous running, not limited to nr of frames. */ +#define MIRA220_IMAGER_RUN_CONT_REG CCI_REG8(0x1002) +#define MIRA220_IMAGER_RUN_CONT_ENABLE 0x04 +#define MIRA220_IMAGER_RUN_CONT_DISABLE 0x00 + +/* Exposure time is indicated in number of rows */ +#define MIRA220_EXP_TIME_REG CCI_REG16_LE(0x100c) + +/* Vertical Blank */ +#define MIRA220_VBLANK_REG CCI_REG16_LE(0x1012) +#define MIRA220_MIN_VBLANK_MARGIN 11 +#define MIRA220_MAX_VBLANK 0xffff + +/* Horizontal flip */ +#define MIRA220_HFLIP_REG CCI_REG8(0x209c) +#define MIRA220_HFLIP_ENABLE_MIRROR 1 +#define MIRA220_HFLIP_DISABLE_MIRROR 0 + +/* Vertical flip */ +#define MIRA220_VFLIP_REG CCI_REG8(0x1095) +#define MIRA220_VFLIP_ENABLE_FLIP 1 +#define MIRA220_VFLIP_DISABLE_FLIP 0 + +#define MIRA220_VSIZE_REG CCI_REG16_LE(0x1087) +#define MIRA220_VSTART_REG CCI_REG16_LE(0x107d) +#define MIRA220_HSIZE_REG CCI_REG16_LE(0x2008) +#define MIRA220_HSIZE_MASK GENMASK(9, 0) +#define MIRA220_HSTART_REG CCI_REG16_LE(0x200a) +#define MIRA220_HSTART_MASK GENMASK(9, 0) +#define MIRA220_MIPI_HSIZE_REG CCI_REG16_LE(0x207d) + +/* MIPI bus control */ +#define MIRA220_MIPI_LANES_REG CCI_REG8(0x6012) + +#define MIRA220_MIPI_CLK_MODE_REG CCI_REG8(0x6013) +#define MIRA220_MIPI_CLK_CONTINUOUS 0x00 +#define MIRA220_MIPI_CLK_NON_CONTINUOUS 0x01 + +/* OTP control */ +#define MIRA220_OTP_CMD_REG CCI_REG8(0x0080) +#define MIRA220_OTP_CMD_UP 0x4 +#define MIRA220_OTP_CMD_DOWN 0x8 + +/* Global sampling time */ +#define MIRA220_GLOB_NUM_CLK_CYCLES 1928 + +/* External clock frequency is 38.4 M */ +#define MIRA220_SUPPORTED_XCLK_FREQ 38400000 + +/* Default exposure is adjusted to mode with smallest height */ +#define MIRA220_DEFAULT_EXPOSURE 1000 +#define MIRA220_MAX_EXPOSURE 0xffff +#define MIRA220_EXPOSURE_MIN 1 + +/* + * Pixel rate is an artificial value. This value is used for timing calcul= ations + * in combination with vblank/hblank. + */ +#define MIRA220_PIXEL_RATE 384000000 + +/* Total line length for 2 lanes calculated for ROW_LENGTH =3D 304. */ +#define MIRA220_LLP_1600x1400_304 3040 + +/* + * ROW_LENGTH: duration of row length in clk_in cycles; set to minimum sup= ported + * value for higher frame rate. + */ +#define MIRA220_REG_ROW_LENGTH CCI_REG16_LE(0x102b) +#define MIRA220_ROW_LENGTH_MIN 304 + +/* MIPI CSI-2 link frequency for 1.5Mbps per lane */ +#define MIRA220_LINK_FREQ_750M 750000000 + +/* Test Pattern */ +#define MIRA220_REG_TEST_PATTERN CCI_REG8(0x2091) +#define MIRA220_TEST_PATTERN_DISABLE 0x00 +#define MIRA220_TEST_PATTERN_VERTICAL_GRADIENT 0x01 + +/* Module variant identifiers */ +#define MIRA220_MONO_PLAIN_PF 0x1e62a032 +#define MIRA220_MONO_PLAIN_NO_PF 0x1e62a045 +#define MIRA220_MONO_AR_PF 0x1e62a036 +#define MIRA220_RGB_PLAIN_PF 0x1e62a03b +#define MIRA220_RGB_PLAIN_NO_PF 0x1e62a046 +#define MIRA220_RGB_AR_PF 0x1e62a03d + +enum mira220_variants { + MIRA220_VARIANT_MONO, + MIRA220_VARIANT_RGB, +}; + +static const s64 mira220_link_freqs[] =3D { + [0] =3D MIRA220_LINK_FREQ_750M, +}; + +static const struct cci_reg_sequence mira220_init_reg_list[] =3D { + /* Base configuration*/ + { CCI_REG8(0x6006), 0x0 }, + { CCI_REG8(0x6012), 0x1 }, + { CCI_REG8(0x6013), 0x0 }, + { CCI_REG8(0x6006), 0x1 }, + { CCI_REG8(0x205d), 0x0 }, + { CCI_REG8(0x2063), 0x0 }, + { CCI_REG8(0x24dc), 0x13 }, + { CCI_REG8(0x24dd), 0x3 }, + { CCI_REG8(0x24de), 0x3 }, + { CCI_REG8(0x24df), 0x0 }, + { CCI_REG8(0x4006), 0x8 }, + { CCI_REG8(0x401c), 0x6f }, + { CCI_REG8(0x204b), 0x3 }, + { CCI_REG8(0x205b), 0x64 }, + { CCI_REG8(0x205c), 0x0 }, + { CCI_REG8(0x4018), 0x3f }, + { CCI_REG8(0x403b), 0xb }, + { CCI_REG8(0x403e), 0xe }, + { CCI_REG8(0x402b), 0x6 }, + { CCI_REG8(0x401e), 0x2 }, + { CCI_REG8(0x4038), 0x3b }, + { CCI_REG8(0x1077), 0x0 }, + { CCI_REG8(0x1078), 0x0 }, + { CCI_REG8(0x1009), 0x8 }, + { CCI_REG8(0x100a), 0x0 }, + { CCI_REG8(0x110f), 0x8 }, + { CCI_REG8(0x1110), 0x0 }, + { CCI_REG8(0x1006), 0x2 }, + { CCI_REG8(0x402c), 0x64 }, + { CCI_REG8(0x3064), 0x0 }, + { CCI_REG8(0x3065), 0xf0 }, + { CCI_REG8(0x4013), 0x13 }, + { CCI_REG8(0x401f), 0x9 }, + { CCI_REG8(0x4020), 0x13 }, + { CCI_REG8(0x4044), 0x75 }, + { CCI_REG8(0x4027), 0x0 }, + { CCI_REG8(0x3215), 0x69 }, + { CCI_REG8(0x3216), 0xf }, + { CCI_REG8(0x322b), 0x69 }, + { CCI_REG8(0x322c), 0xf }, + { CCI_REG8(0x4051), 0x80 }, + { CCI_REG8(0x4052), 0x10 }, + { CCI_REG8(0x4057), 0x80 }, + { CCI_REG8(0x4058), 0x10 }, + { CCI_REG8(0x3212), 0x59 }, + { CCI_REG8(0x4047), 0x8f }, + { CCI_REG8(0x4026), 0x10 }, + { CCI_REG8(0x4032), 0x53 }, + { CCI_REG8(0x4036), 0x17 }, + { CCI_REG8(0x50b8), 0xf4 }, + { CCI_REG8(0x3016), 0x0 }, + { CCI_REG8(0x3017), 0x2c }, + { CCI_REG8(0x3018), 0x8c }, + { CCI_REG8(0x3019), 0x45 }, + { CCI_REG8(0x301a), 0x5 }, + { CCI_REG8(0x3013), 0xa }, + { CCI_REG8(0x301b), 0x0 }, + { CCI_REG8(0x301c), 0x4 }, + { CCI_REG8(0x301d), 0x88 }, + { CCI_REG8(0x301e), 0x45 }, + { CCI_REG8(0x301f), 0x5 }, + { CCI_REG8(0x3020), 0x0 }, + { CCI_REG8(0x3021), 0x4 }, + { CCI_REG8(0x3022), 0x88 }, + { CCI_REG8(0x3023), 0x45 }, + { CCI_REG8(0x3024), 0x5 }, + { CCI_REG8(0x3025), 0x0 }, + { CCI_REG8(0x3026), 0x4 }, + { CCI_REG8(0x3027), 0x88 }, + { CCI_REG8(0x3028), 0x45 }, + { CCI_REG8(0x3029), 0x5 }, + { CCI_REG8(0x302f), 0x0 }, + { CCI_REG8(0x3056), 0x0 }, + { CCI_REG8(0x3057), 0x0 }, + { CCI_REG8(0x3300), 0x1 }, + { CCI_REG8(0x3301), 0x0 }, + { CCI_REG8(0x3302), 0xb0 }, + { CCI_REG8(0x3303), 0xb0 }, + { CCI_REG8(0x3304), 0x16 }, + { CCI_REG8(0x3305), 0x15 }, + { CCI_REG8(0x3306), 0x1 }, + { CCI_REG8(0x3307), 0x0 }, + { CCI_REG8(0x3308), 0x30 }, + { CCI_REG8(0x3309), 0xa0 }, + { CCI_REG8(0x330a), 0x16 }, + { CCI_REG8(0x330b), 0x15 }, + { CCI_REG8(0x330c), 0x1 }, + { CCI_REG8(0x330d), 0x0 }, + { CCI_REG8(0x330e), 0x30 }, + { CCI_REG8(0x330f), 0xa0 }, + { CCI_REG8(0x3310), 0x16 }, + { CCI_REG8(0x3311), 0x15 }, + { CCI_REG8(0x3312), 0x1 }, + { CCI_REG8(0x3313), 0x0 }, + { CCI_REG8(0x3314), 0x30 }, + { CCI_REG8(0x3315), 0xa0 }, + { CCI_REG8(0x3316), 0x16 }, + { CCI_REG8(0x3317), 0x15 }, + { CCI_REG8(0x3318), 0x1 }, + { CCI_REG8(0x3319), 0x0 }, + { CCI_REG8(0x331a), 0x30 }, + { CCI_REG8(0x331b), 0xa0 }, + { CCI_REG8(0x331c), 0x16 }, + { CCI_REG8(0x331d), 0x15 }, + { CCI_REG8(0x331e), 0x1 }, + { CCI_REG8(0x331f), 0x0 }, + { CCI_REG8(0x3320), 0x30 }, + { CCI_REG8(0x3321), 0xa0 }, + { CCI_REG8(0x3322), 0x16 }, + { CCI_REG8(0x3323), 0x15 }, + { CCI_REG8(0x3324), 0x1 }, + { CCI_REG8(0x3325), 0x0 }, + { CCI_REG8(0x3326), 0x30 }, + { CCI_REG8(0x3327), 0xa0 }, + { CCI_REG8(0x3328), 0x16 }, + { CCI_REG8(0x3329), 0x15 }, + { CCI_REG8(0x332a), 0x2b }, + { CCI_REG8(0x332b), 0x0 }, + { CCI_REG8(0x332c), 0x30 }, + { CCI_REG8(0x332d), 0xa0 }, + { CCI_REG8(0x332e), 0x16 }, + { CCI_REG8(0x332f), 0x15 }, + { CCI_REG8(0x3330), 0x1 }, + { CCI_REG8(0x3331), 0x0 }, + { CCI_REG8(0x3332), 0x10 }, + { CCI_REG8(0x3333), 0xa0 }, + { CCI_REG8(0x3334), 0x16 }, + { CCI_REG8(0x3335), 0x15 }, + { CCI_REG8(0x3058), 0x8 }, + { CCI_REG8(0x3059), 0x0 }, + { CCI_REG8(0x305a), 0x9 }, + { CCI_REG8(0x305b), 0x0 }, + { CCI_REG8(0x3336), 0x1 }, + { CCI_REG8(0x3337), 0x0 }, + { CCI_REG8(0x3338), 0x90 }, + { CCI_REG8(0x3339), 0xb0 }, + { CCI_REG8(0x333a), 0x16 }, + { CCI_REG8(0x333b), 0x15 }, + { CCI_REG8(0x333c), 0x1f }, + { CCI_REG8(0x333d), 0x0 }, + { CCI_REG8(0x333e), 0x10 }, + { CCI_REG8(0x333f), 0xa0 }, + { CCI_REG8(0x3340), 0x16 }, + { CCI_REG8(0x3341), 0x15 }, + { CCI_REG8(0x3342), 0x52 }, + { CCI_REG8(0x3343), 0x0 }, + { CCI_REG8(0x3344), 0x10 }, + { CCI_REG8(0x3345), 0x80 }, + { CCI_REG8(0x3346), 0x16 }, + { CCI_REG8(0x3347), 0x15 }, + { CCI_REG8(0x3348), 0x1 }, + { CCI_REG8(0x3349), 0x0 }, + { CCI_REG8(0x334a), 0x10 }, + { CCI_REG8(0x334b), 0x80 }, + { CCI_REG8(0x334c), 0x16 }, + { CCI_REG8(0x334d), 0x1d }, + { CCI_REG8(0x334e), 0x1 }, + { CCI_REG8(0x334f), 0x0 }, + { CCI_REG8(0x3350), 0x50 }, + { CCI_REG8(0x3351), 0x84 }, + { CCI_REG8(0x3352), 0x16 }, + { CCI_REG8(0x3353), 0x1d }, + { CCI_REG8(0x3354), 0x18 }, + { CCI_REG8(0x3355), 0x0 }, + { CCI_REG8(0x3356), 0x10 }, + { CCI_REG8(0x3357), 0x84 }, + { CCI_REG8(0x3358), 0x16 }, + { CCI_REG8(0x3359), 0x1d }, + { CCI_REG8(0x335a), 0x80 }, + { CCI_REG8(0x335b), 0x2 }, + { CCI_REG8(0x335c), 0x10 }, + { CCI_REG8(0x335d), 0xc4 }, + { CCI_REG8(0x335e), 0x14 }, + { CCI_REG8(0x335f), 0x1d }, + { CCI_REG8(0x3360), 0xa5 }, + { CCI_REG8(0x3361), 0x0 }, + { CCI_REG8(0x3362), 0x10 }, + { CCI_REG8(0x3363), 0x84 }, + { CCI_REG8(0x3364), 0x16 }, + { CCI_REG8(0x3365), 0x1d }, + { CCI_REG8(0x3366), 0x1 }, + { CCI_REG8(0x3367), 0x0 }, + { CCI_REG8(0x3368), 0x90 }, + { CCI_REG8(0x3369), 0x84 }, + { CCI_REG8(0x336a), 0x16 }, + { CCI_REG8(0x336b), 0x1d }, + { CCI_REG8(0x336c), 0x12 }, + { CCI_REG8(0x336d), 0x0 }, + { CCI_REG8(0x336e), 0x10 }, + { CCI_REG8(0x336f), 0x84 }, + { CCI_REG8(0x3370), 0x16 }, + { CCI_REG8(0x3371), 0x15 }, + { CCI_REG8(0x3372), 0x32 }, + { CCI_REG8(0x3373), 0x0 }, + { CCI_REG8(0x3374), 0x30 }, + { CCI_REG8(0x3375), 0x84 }, + { CCI_REG8(0x3376), 0x16 }, + { CCI_REG8(0x3377), 0x15 }, + { CCI_REG8(0x3378), 0x26 }, + { CCI_REG8(0x3379), 0x0 }, + { CCI_REG8(0x337a), 0x10 }, + { CCI_REG8(0x337b), 0x84 }, + { CCI_REG8(0x337c), 0x16 }, + { CCI_REG8(0x337d), 0x15 }, + { CCI_REG8(0x337e), 0x80 }, + { CCI_REG8(0x337f), 0x2 }, + { CCI_REG8(0x3380), 0x10 }, + { CCI_REG8(0x3381), 0xc4 }, + { CCI_REG8(0x3382), 0x14 }, + { CCI_REG8(0x3383), 0x15 }, + { CCI_REG8(0x3384), 0xa9 }, + { CCI_REG8(0x3385), 0x0 }, + { CCI_REG8(0x3386), 0x10 }, + { CCI_REG8(0x3387), 0x84 }, + { CCI_REG8(0x3388), 0x16 }, + { CCI_REG8(0x3389), 0x15 }, + { CCI_REG8(0x338a), 0x41 }, + { CCI_REG8(0x338b), 0x0 }, + { CCI_REG8(0x338c), 0x10 }, + { CCI_REG8(0x338d), 0x80 }, + { CCI_REG8(0x338e), 0x16 }, + { CCI_REG8(0x338f), 0x15 }, + { CCI_REG8(0x3390), 0x2 }, + { CCI_REG8(0x3391), 0x0 }, + { CCI_REG8(0x3392), 0x10 }, + { CCI_REG8(0x3393), 0xa0 }, + { CCI_REG8(0x3394), 0x16 }, + { CCI_REG8(0x3395), 0x15 }, + { CCI_REG8(0x305c), 0x18 }, + { CCI_REG8(0x305d), 0x0 }, + { CCI_REG8(0x305e), 0x19 }, + { CCI_REG8(0x305f), 0x0 }, + { CCI_REG8(0x3396), 0x1 }, + { CCI_REG8(0x3397), 0x0 }, + { CCI_REG8(0x3398), 0x90 }, + { CCI_REG8(0x3399), 0x30 }, + { CCI_REG8(0x339a), 0x56 }, + { CCI_REG8(0x339b), 0x57 }, + { CCI_REG8(0x339c), 0x1 }, + { CCI_REG8(0x339d), 0x0 }, + { CCI_REG8(0x339e), 0x10 }, + { CCI_REG8(0x339f), 0x20 }, + { CCI_REG8(0x33a0), 0xd6 }, + { CCI_REG8(0x33a1), 0x17 }, + { CCI_REG8(0x33a2), 0x1 }, + { CCI_REG8(0x33a3), 0x0 }, + { CCI_REG8(0x33a4), 0x10 }, + { CCI_REG8(0x33a5), 0x28 }, + { CCI_REG8(0x33a6), 0xd6 }, + { CCI_REG8(0x33a7), 0x17 }, + { CCI_REG8(0x33a8), 0x3 }, + { CCI_REG8(0x33a9), 0x0 }, + { CCI_REG8(0x33aa), 0x10 }, + { CCI_REG8(0x33ab), 0x20 }, + { CCI_REG8(0x33ac), 0xd6 }, + { CCI_REG8(0x33ad), 0x17 }, + { CCI_REG8(0x33ae), 0x61 }, + { CCI_REG8(0x33af), 0x0 }, + { CCI_REG8(0x33b0), 0x10 }, + { CCI_REG8(0x33b1), 0x20 }, + { CCI_REG8(0x33b2), 0xd6 }, + { CCI_REG8(0x33b3), 0x15 }, + { CCI_REG8(0x33b4), 0x1 }, + { CCI_REG8(0x33b5), 0x0 }, + { CCI_REG8(0x33b6), 0x10 }, + { CCI_REG8(0x33b7), 0x20 }, + { CCI_REG8(0x33b8), 0xd6 }, + { CCI_REG8(0x33b9), 0x1d }, + { CCI_REG8(0x33ba), 0x1 }, + { CCI_REG8(0x33bb), 0x0 }, + { CCI_REG8(0x33bc), 0x50 }, + { CCI_REG8(0x33bd), 0x20 }, + { CCI_REG8(0x33be), 0xd6 }, + { CCI_REG8(0x33bf), 0x1d }, + { CCI_REG8(0x33c0), 0x2c }, + { CCI_REG8(0x33c1), 0x0 }, + { CCI_REG8(0x33c2), 0x10 }, + { CCI_REG8(0x33c3), 0x20 }, + { CCI_REG8(0x33c4), 0xd6 }, + { CCI_REG8(0x33c5), 0x1d }, + { CCI_REG8(0x33c6), 0x1 }, + { CCI_REG8(0x33c7), 0x0 }, + { CCI_REG8(0x33c8), 0x90 }, + { CCI_REG8(0x33c9), 0x20 }, + { CCI_REG8(0x33ca), 0xd6 }, + { CCI_REG8(0x33cb), 0x1d }, + { CCI_REG8(0x33cc), 0x83 }, + { CCI_REG8(0x33cd), 0x0 }, + { CCI_REG8(0x33ce), 0x10 }, + { CCI_REG8(0x33cf), 0x20 }, + { CCI_REG8(0x33d0), 0xd6 }, + { CCI_REG8(0x33d1), 0x15 }, + { CCI_REG8(0x33d2), 0x1 }, + { CCI_REG8(0x33d3), 0x0 }, + { CCI_REG8(0x33d4), 0x10 }, + { CCI_REG8(0x33d5), 0x30 }, + { CCI_REG8(0x33d6), 0xd6 }, + { CCI_REG8(0x33d7), 0x15 }, + { CCI_REG8(0x33d8), 0x1 }, + { CCI_REG8(0x33d9), 0x0 }, + { CCI_REG8(0x33da), 0x10 }, + { CCI_REG8(0x33db), 0x20 }, + { CCI_REG8(0x33dc), 0xd6 }, + { CCI_REG8(0x33dd), 0x15 }, + { CCI_REG8(0x33de), 0x1 }, + { CCI_REG8(0x33df), 0x0 }, + { CCI_REG8(0x33e0), 0x10 }, + { CCI_REG8(0x33e1), 0x20 }, + { CCI_REG8(0x33e2), 0x56 }, + { CCI_REG8(0x33e3), 0x15 }, + { CCI_REG8(0x33e4), 0x7 }, + { CCI_REG8(0x33e5), 0x0 }, + { CCI_REG8(0x33e6), 0x10 }, + { CCI_REG8(0x33e7), 0x20 }, + { CCI_REG8(0x33e8), 0x16 }, + { CCI_REG8(0x33e9), 0x15 }, + { CCI_REG8(0x3060), 0x26 }, + { CCI_REG8(0x3061), 0x0 }, + { CCI_REG8(0x302a), 0xff }, + { CCI_REG8(0x302b), 0xff }, + { CCI_REG8(0x302c), 0xff }, + { CCI_REG8(0x302d), 0xff }, + { CCI_REG8(0x302e), 0x3f }, + { CCI_REG8(0x3013), 0xb }, + { CCI_REG8(0x102b), 0x2c }, + { CCI_REG8(0x102c), 0x1 }, + { CCI_REG8(0x1035), 0x54 }, + { CCI_REG8(0x1036), 0x0 }, + { CCI_REG8(0x3090), 0x2a }, + { CCI_REG8(0x3091), 0x1 }, + { CCI_REG8(0x30c6), 0x5 }, + { CCI_REG8(0x30c7), 0x0 }, + { CCI_REG8(0x30c8), 0x0 }, + { CCI_REG8(0x30c9), 0x0 }, + { CCI_REG8(0x30ca), 0x0 }, + { CCI_REG8(0x30cb), 0x0 }, + { CCI_REG8(0x30cc), 0x0 }, + { CCI_REG8(0x30cd), 0x0 }, + { CCI_REG8(0x30ce), 0x0 }, + { CCI_REG8(0x30cf), 0x5 }, + { CCI_REG8(0x30d0), 0x0 }, + { CCI_REG8(0x30d1), 0x0 }, + { CCI_REG8(0x30d2), 0x0 }, + { CCI_REG8(0x30d3), 0x0 }, + { CCI_REG8(0x30d4), 0x0 }, + { CCI_REG8(0x30d5), 0x0 }, + { CCI_REG8(0x30d6), 0x0 }, + { CCI_REG8(0x30d7), 0x0 }, + { CCI_REG8(0x30f3), 0x5 }, + { CCI_REG8(0x30f4), 0x0 }, + { CCI_REG8(0x30f5), 0x0 }, + { CCI_REG8(0x30f6), 0x0 }, + { CCI_REG8(0x30f7), 0x0 }, + { CCI_REG8(0x30f8), 0x0 }, + { CCI_REG8(0x30f9), 0x0 }, + { CCI_REG8(0x30fa), 0x0 }, + { CCI_REG8(0x30fb), 0x0 }, + { CCI_REG8(0x30d8), 0x5 }, + { CCI_REG8(0x30d9), 0x0 }, + { CCI_REG8(0x30da), 0x0 }, + { CCI_REG8(0x30db), 0x0 }, + { CCI_REG8(0x30dc), 0x0 }, + { CCI_REG8(0x30dd), 0x0 }, + { CCI_REG8(0x30de), 0x0 }, + { CCI_REG8(0x30df), 0x0 }, + { CCI_REG8(0x30e0), 0x0 }, + { CCI_REG8(0x30e1), 0x5 }, + { CCI_REG8(0x30e2), 0x0 }, + { CCI_REG8(0x30e3), 0x0 }, + { CCI_REG8(0x30e4), 0x0 }, + { CCI_REG8(0x30e5), 0x0 }, + { CCI_REG8(0x30e6), 0x0 }, + { CCI_REG8(0x30e7), 0x0 }, + { CCI_REG8(0x30e8), 0x0 }, + { CCI_REG8(0x30e9), 0x0 }, + { CCI_REG8(0x30f3), 0x5 }, + { CCI_REG8(0x30f4), 0x2 }, + { CCI_REG8(0x30f5), 0x0 }, + { CCI_REG8(0x30f6), 0x17 }, + { CCI_REG8(0x30f7), 0x1 }, + { CCI_REG8(0x30f8), 0x0 }, + { CCI_REG8(0x30f9), 0x0 }, + { CCI_REG8(0x30fa), 0x0 }, + { CCI_REG8(0x30fb), 0x0 }, + { CCI_REG8(0x30d8), 0x3 }, + { CCI_REG8(0x30d9), 0x1 }, + { CCI_REG8(0x30da), 0x0 }, + { CCI_REG8(0x30db), 0x19 }, + { CCI_REG8(0x30dc), 0x1 }, + { CCI_REG8(0x30dd), 0x0 }, + { CCI_REG8(0x30de), 0x0 }, + { CCI_REG8(0x30df), 0x0 }, + { CCI_REG8(0x30e0), 0x0 }, + { CCI_REG8(0x30a2), 0x5 }, + { CCI_REG8(0x30a3), 0x2 }, + { CCI_REG8(0x30a4), 0x0 }, + { CCI_REG8(0x30a5), 0x22 }, + { CCI_REG8(0x30a6), 0x0 }, + { CCI_REG8(0x30a7), 0x0 }, + { CCI_REG8(0x30a8), 0x0 }, + { CCI_REG8(0x30a9), 0x0 }, + { CCI_REG8(0x30aa), 0x0 }, + { CCI_REG8(0x30ab), 0x5 }, + { CCI_REG8(0x30ac), 0x2 }, + { CCI_REG8(0x30ad), 0x0 }, + { CCI_REG8(0x30ae), 0x22 }, + { CCI_REG8(0x30af), 0x0 }, + { CCI_REG8(0x30b0), 0x0 }, + { CCI_REG8(0x30b1), 0x0 }, + { CCI_REG8(0x30b2), 0x0 }, + { CCI_REG8(0x30b3), 0x0 }, + { CCI_REG8(0x30bd), 0x5 }, + { CCI_REG8(0x30be), 0x9f }, + { CCI_REG8(0x30bf), 0x0 }, + { CCI_REG8(0x30c0), 0x7d }, + { CCI_REG8(0x30c1), 0x0 }, + { CCI_REG8(0x30c2), 0x0 }, + { CCI_REG8(0x30c3), 0x0 }, + { CCI_REG8(0x30c4), 0x0 }, + { CCI_REG8(0x30c5), 0x0 }, + { CCI_REG8(0x30b4), 0x4 }, + { CCI_REG8(0x30b5), 0x9c }, + { CCI_REG8(0x30b6), 0x0 }, + { CCI_REG8(0x30b7), 0x7d }, + { CCI_REG8(0x30b8), 0x0 }, + { CCI_REG8(0x30b9), 0x0 }, + { CCI_REG8(0x30ba), 0x0 }, + { CCI_REG8(0x30bb), 0x0 }, + { CCI_REG8(0x30bc), 0x0 }, + { CCI_REG8(0x30fc), 0x5 }, + { CCI_REG8(0x30fd), 0x0 }, + { CCI_REG8(0x30fe), 0x0 }, + { CCI_REG8(0x30ff), 0x0 }, + { CCI_REG8(0x3100), 0x0 }, + { CCI_REG8(0x3101), 0x0 }, + { CCI_REG8(0x3102), 0x0 }, + { CCI_REG8(0x3103), 0x0 }, + { CCI_REG8(0x3104), 0x0 }, + { CCI_REG8(0x3105), 0x5 }, + { CCI_REG8(0x3106), 0x0 }, + { CCI_REG8(0x3107), 0x0 }, + { CCI_REG8(0x3108), 0x0 }, + { CCI_REG8(0x3109), 0x0 }, + { CCI_REG8(0x310a), 0x0 }, + { CCI_REG8(0x310b), 0x0 }, + { CCI_REG8(0x310c), 0x0 }, + { CCI_REG8(0x310d), 0x0 }, + { CCI_REG8(0x3099), 0x5 }, + { CCI_REG8(0x309a), 0x96 }, + { CCI_REG8(0x309b), 0x0 }, + { CCI_REG8(0x309c), 0x6 }, + { CCI_REG8(0x309d), 0x0 }, + { CCI_REG8(0x309e), 0x0 }, + { CCI_REG8(0x309f), 0x0 }, + { CCI_REG8(0x30a0), 0x0 }, + { CCI_REG8(0x30a1), 0x0 }, + { CCI_REG8(0x310e), 0x5 }, + { CCI_REG8(0x310f), 0x2 }, + { CCI_REG8(0x3110), 0x0 }, + { CCI_REG8(0x3111), 0x2b }, + { CCI_REG8(0x3112), 0x0 }, + { CCI_REG8(0x3113), 0x0 }, + { CCI_REG8(0x3114), 0x0 }, + { CCI_REG8(0x3115), 0x0 }, + { CCI_REG8(0x3116), 0x0 }, + { CCI_REG8(0x3117), 0x5 }, + { CCI_REG8(0x3118), 0x2 }, + { CCI_REG8(0x3119), 0x0 }, + { CCI_REG8(0x311a), 0x2c }, + { CCI_REG8(0x311b), 0x0 }, + { CCI_REG8(0x311c), 0x0 }, + { CCI_REG8(0x311d), 0x0 }, + { CCI_REG8(0x311e), 0x0 }, + { CCI_REG8(0x311f), 0x0 }, + { CCI_REG8(0x30ea), 0x0 }, + { CCI_REG8(0x30eb), 0x0 }, + { CCI_REG8(0x30ec), 0x0 }, + { CCI_REG8(0x30ed), 0x0 }, + { CCI_REG8(0x30ee), 0x0 }, + { CCI_REG8(0x30ef), 0x0 }, + { CCI_REG8(0x30f0), 0x0 }, + { CCI_REG8(0x30f1), 0x0 }, + { CCI_REG8(0x30f2), 0x0 }, + { CCI_REG8(0x313b), 0x3 }, + { CCI_REG8(0x313c), 0x31 }, + { CCI_REG8(0x313d), 0x0 }, + { CCI_REG8(0x313e), 0x7 }, + { CCI_REG8(0x313f), 0x0 }, + { CCI_REG8(0x3140), 0x68 }, + { CCI_REG8(0x3141), 0x0 }, + { CCI_REG8(0x3142), 0x34 }, + { CCI_REG8(0x3143), 0x0 }, + { CCI_REG8(0x31a0), 0x3 }, + { CCI_REG8(0x31a1), 0x16 }, + { CCI_REG8(0x31a2), 0x0 }, + { CCI_REG8(0x31a3), 0x8 }, + { CCI_REG8(0x31a4), 0x0 }, + { CCI_REG8(0x31a5), 0x7e }, + { CCI_REG8(0x31a6), 0x0 }, + { CCI_REG8(0x31a7), 0x8 }, + { CCI_REG8(0x31a8), 0x0 }, + { CCI_REG8(0x31a9), 0x3 }, + { CCI_REG8(0x31aa), 0x16 }, + { CCI_REG8(0x31ab), 0x0 }, + { CCI_REG8(0x31ac), 0x8 }, + { CCI_REG8(0x31ad), 0x0 }, + { CCI_REG8(0x31ae), 0x7e }, + { CCI_REG8(0x31af), 0x0 }, + { CCI_REG8(0x31b0), 0x8 }, + { CCI_REG8(0x31b1), 0x0 }, + { CCI_REG8(0x31b2), 0x3 }, + { CCI_REG8(0x31b3), 0x16 }, + { CCI_REG8(0x31b4), 0x0 }, + { CCI_REG8(0x31b5), 0x8 }, + { CCI_REG8(0x31b6), 0x0 }, + { CCI_REG8(0x31b7), 0x7e }, + { CCI_REG8(0x31b8), 0x0 }, + { CCI_REG8(0x31b9), 0x8 }, + { CCI_REG8(0x31ba), 0x0 }, + { CCI_REG8(0x3120), 0x5 }, + { CCI_REG8(0x3121), 0x45 }, + { CCI_REG8(0x3122), 0x0 }, + { CCI_REG8(0x3123), 0x1d }, + { CCI_REG8(0x3124), 0x0 }, + { CCI_REG8(0x3125), 0xa9 }, + { CCI_REG8(0x3126), 0x0 }, + { CCI_REG8(0x3127), 0x6d }, + { CCI_REG8(0x3128), 0x0 }, + { CCI_REG8(0x3129), 0x5 }, + { CCI_REG8(0x312a), 0x15 }, + { CCI_REG8(0x312b), 0x0 }, + { CCI_REG8(0x312c), 0xa }, + { CCI_REG8(0x312d), 0x0 }, + { CCI_REG8(0x312e), 0x45 }, + { CCI_REG8(0x312f), 0x0 }, + { CCI_REG8(0x3130), 0x1d }, + { CCI_REG8(0x3131), 0x0 }, + { CCI_REG8(0x3132), 0x5 }, + { CCI_REG8(0x3133), 0x7d }, + { CCI_REG8(0x3134), 0x0 }, + { CCI_REG8(0x3135), 0xa }, + { CCI_REG8(0x3136), 0x0 }, + { CCI_REG8(0x3137), 0xa9 }, + { CCI_REG8(0x3138), 0x0 }, + { CCI_REG8(0x3139), 0x6d }, + { CCI_REG8(0x313a), 0x0 }, + { CCI_REG8(0x3144), 0x5 }, + { CCI_REG8(0x3145), 0x0 }, + { CCI_REG8(0x3146), 0x0 }, + { CCI_REG8(0x3147), 0x30 }, + { CCI_REG8(0x3148), 0x0 }, + { CCI_REG8(0x3149), 0x0 }, + { CCI_REG8(0x314a), 0x0 }, + { CCI_REG8(0x314b), 0x0 }, + { CCI_REG8(0x314c), 0x0 }, + { CCI_REG8(0x314d), 0x3 }, + { CCI_REG8(0x314e), 0x0 }, + { CCI_REG8(0x314f), 0x0 }, + { CCI_REG8(0x3150), 0x31 }, + { CCI_REG8(0x3151), 0x0 }, + { CCI_REG8(0x3152), 0x0 }, + { CCI_REG8(0x3153), 0x0 }, + { CCI_REG8(0x3154), 0x0 }, + { CCI_REG8(0x3155), 0x0 }, + { CCI_REG8(0x31d8), 0x5 }, + { CCI_REG8(0x31d9), 0x3a }, + { CCI_REG8(0x31da), 0x0 }, + { CCI_REG8(0x31db), 0x2e }, + { CCI_REG8(0x31dc), 0x0 }, + { CCI_REG8(0x31dd), 0x9e }, + { CCI_REG8(0x31de), 0x0 }, + { CCI_REG8(0x31df), 0x7e }, + { CCI_REG8(0x31e0), 0x0 }, + { CCI_REG8(0x31e1), 0x5 }, + { CCI_REG8(0x31e2), 0x4 }, + { CCI_REG8(0x31e3), 0x0 }, + { CCI_REG8(0x31e4), 0x4 }, + { CCI_REG8(0x31e5), 0x0 }, + { CCI_REG8(0x31e6), 0x73 }, + { CCI_REG8(0x31e7), 0x0 }, + { CCI_REG8(0x31e8), 0x4 }, + { CCI_REG8(0x31e9), 0x0 }, + { CCI_REG8(0x31ea), 0x5 }, + { CCI_REG8(0x31eb), 0x0 }, + { CCI_REG8(0x31ec), 0x0 }, + { CCI_REG8(0x31ed), 0x0 }, + { CCI_REG8(0x31ee), 0x0 }, + { CCI_REG8(0x31ef), 0x0 }, + { CCI_REG8(0x31f0), 0x0 }, + { CCI_REG8(0x31f1), 0x0 }, + { CCI_REG8(0x31f2), 0x0 }, + { CCI_REG8(0x31f3), 0x0 }, + { CCI_REG8(0x31f4), 0x0 }, + { CCI_REG8(0x31f5), 0x0 }, + { CCI_REG8(0x31f6), 0x0 }, + { CCI_REG8(0x31f7), 0x0 }, + { CCI_REG8(0x31f8), 0x0 }, + { CCI_REG8(0x31f9), 0x0 }, + { CCI_REG8(0x31fa), 0x0 }, + { CCI_REG8(0x31fb), 0x5 }, + { CCI_REG8(0x31fc), 0x0 }, + { CCI_REG8(0x31fd), 0x0 }, + { CCI_REG8(0x31fe), 0x0 }, + { CCI_REG8(0x31ff), 0x0 }, + { CCI_REG8(0x3200), 0x0 }, + { CCI_REG8(0x3201), 0x0 }, + { CCI_REG8(0x3202), 0x0 }, + { CCI_REG8(0x3203), 0x0 }, + { CCI_REG8(0x3204), 0x0 }, + { CCI_REG8(0x3205), 0x0 }, + { CCI_REG8(0x3206), 0x0 }, + { CCI_REG8(0x3207), 0x0 }, + { CCI_REG8(0x3208), 0x0 }, + { CCI_REG8(0x3209), 0x0 }, + { CCI_REG8(0x320a), 0x0 }, + { CCI_REG8(0x320b), 0x0 }, + { CCI_REG8(0x3164), 0x5 }, + { CCI_REG8(0x3165), 0x14 }, + { CCI_REG8(0x3166), 0x0 }, + { CCI_REG8(0x3167), 0xc }, + { CCI_REG8(0x3168), 0x0 }, + { CCI_REG8(0x3169), 0x44 }, + { CCI_REG8(0x316a), 0x0 }, + { CCI_REG8(0x316b), 0x1f }, + { CCI_REG8(0x316c), 0x0 }, + { CCI_REG8(0x316d), 0x5 }, + { CCI_REG8(0x316e), 0x7c }, + { CCI_REG8(0x316f), 0x0 }, + { CCI_REG8(0x3170), 0xc }, + { CCI_REG8(0x3171), 0x0 }, + { CCI_REG8(0x3172), 0xa8 }, + { CCI_REG8(0x3173), 0x0 }, + { CCI_REG8(0x3174), 0x6f }, + { CCI_REG8(0x3175), 0x0 }, + { CCI_REG8(0x31c4), 0x5 }, + { CCI_REG8(0x31c5), 0x24 }, + { CCI_REG8(0x31c6), 0x1 }, + { CCI_REG8(0x31c7), 0x4 }, + { CCI_REG8(0x31c8), 0x0 }, + { CCI_REG8(0x31c9), 0x5 }, + { CCI_REG8(0x31ca), 0x24 }, + { CCI_REG8(0x31cb), 0x1 }, + { CCI_REG8(0x31cc), 0x4 }, + { CCI_REG8(0x31cd), 0x0 }, + { CCI_REG8(0x31ce), 0x5 }, + { CCI_REG8(0x31cf), 0x24 }, + { CCI_REG8(0x31d0), 0x1 }, + { CCI_REG8(0x31d1), 0x4 }, + { CCI_REG8(0x31d2), 0x0 }, + { CCI_REG8(0x31d3), 0x5 }, + { CCI_REG8(0x31d4), 0x73 }, + { CCI_REG8(0x31d5), 0x0 }, + { CCI_REG8(0x31d6), 0xb1 }, + { CCI_REG8(0x31d7), 0x0 }, + { CCI_REG8(0x3176), 0x5 }, + { CCI_REG8(0x3177), 0x10 }, + { CCI_REG8(0x3178), 0x0 }, + { CCI_REG8(0x3179), 0x56 }, + { CCI_REG8(0x317a), 0x0 }, + { CCI_REG8(0x317b), 0x0 }, + { CCI_REG8(0x317c), 0x0 }, + { CCI_REG8(0x317d), 0x0 }, + { CCI_REG8(0x317e), 0x0 }, + { CCI_REG8(0x317f), 0x5 }, + { CCI_REG8(0x3180), 0x6a }, + { CCI_REG8(0x3181), 0x0 }, + { CCI_REG8(0x3182), 0xad }, + { CCI_REG8(0x3183), 0x0 }, + { CCI_REG8(0x3184), 0x0 }, + { CCI_REG8(0x3185), 0x0 }, + { CCI_REG8(0x3186), 0x0 }, + { CCI_REG8(0x3187), 0x0 }, + { CCI_REG8(0x100d), 0x0 }, + { CCI_REG8(0x1013), 0x2b }, + /* Sensor control mode */ + { CCI_REG8(0x0043), 0x0 }, // Sensor Control Mode.SLEEP_POWER_MODE(0) + { CCI_REG8(0x0043), 0x0 }, // Sensor Control Mode.IDLE_POWER_MODE(0) + { CCI_REG8(0x0043), 0x4 }, // Sensor Control Mode.SYSTEM_CLOCK_ENABLE(0) + { CCI_REG8(0x0043), 0xC }, // Sensor Control Mode.SRAM_CLOCK_ENABLE(0) + { CCI_REG8(0x1001), 0x41 }, // Sensor Control Mode.EXT_EVENT_SEL(0) + { CCI_REG8(0x10f2), 0x1 }, // Sensor Control Mode.NB_OF_FRAMES_A(0) + { CCI_REG8(0x10f3), 0x0 }, // Sensor Control Mode.NB_OF_FRAMES_A(1) + { CCI_REG8(0x0012), 0x0 }, // IO Drive Strength.DIG_DRIVE_STRENGTH(0) + { CCI_REG8(0x0012), 0x0 }, // IO Drive Strength.CCI_DRIVE_STRENGTH(0) + { CCI_REG8(0x1001), 0x41 }, // Readout && Exposure.EXT_EXP_PW_SEL(0) + { CCI_REG8(0x10d0), 0x0 }, // Readout && Exposure.EXT_EXP_PW_DELAY(0) + { CCI_REG8(0x10d1), 0x0 }, // Readout && Exposure.EXT_EXP_PW_DELAY(1) + /* MIPI */ + { CCI_REG8(0x6006), 0x0 }, // MIPI.TX_CTRL_EN(0) + { CCI_REG8(0x5004), 0x1 }, // MIPI.datarate + { CCI_REG8(0x5086), 0x2 }, // MIPI.datarate + { CCI_REG8(0x5087), 0x4e }, // MIPI.datarate + { CCI_REG8(0x5088), 0x0 }, // MIPI.datarate + { CCI_REG8(0x5090), 0x0 }, // MIPI.datarate + { CCI_REG8(0x5091), 0x8 }, // MIPI.datarate + { CCI_REG8(0x5092), 0x14 }, // MIPI.datarate + { CCI_REG8(0x5093), 0xf }, // MIPI.datarate + { CCI_REG8(0x5094), 0x6 }, // MIPI.datarate + { CCI_REG8(0x5095), 0x32 }, // MIPI.datarate + { CCI_REG8(0x5096), 0xe }, // MIPI.datarate + { CCI_REG8(0x5097), 0x0 }, // MIPI.datarate + { CCI_REG8(0x5098), 0x11 }, // MIPI.datarate + { CCI_REG8(0x5004), 0x0 }, // MIPI.datarate + { CCI_REG8(0x2066), 0x6c }, // MIPI.datarate + { CCI_REG8(0x2067), 0x7 }, // MIPI.datarate + { CCI_REG8(0x206e), 0x7e }, // MIPI.datarate + { CCI_REG8(0x206f), 0x6 }, // MIPI.datarate + { CCI_REG8(0x20ac), 0x7e }, // MIPI.datarate + { CCI_REG8(0x20ad), 0x6 }, // MIPI.datarate + { CCI_REG8(0x2076), 0xc8 }, // MIPI.datarate + { CCI_REG8(0x2077), 0x0 }, // MIPI.datarate + { CCI_REG8(0x20b4), 0xc8 }, // MIPI.datarate + { CCI_REG8(0x20b5), 0x0 }, // MIPI.datarate + { CCI_REG8(0x2078), 0x1e }, // MIPI.datarate + { CCI_REG8(0x2079), 0x4 }, // MIPI.datarate + { CCI_REG8(0x20b6), 0x1e }, // MIPI.datarate + { CCI_REG8(0x20b7), 0x4 }, // MIPI.datarate + { CCI_REG8(0x207a), 0xd4 }, // MIPI.datarate + { CCI_REG8(0x207b), 0x4 }, // MIPI.datarate + { CCI_REG8(0x20b8), 0xd4 }, // MIPI.datarate + { CCI_REG8(0x20b9), 0x4 }, // MIPI.datarate + { CCI_REG8(0x207c), 0x0 }, // MIPI.VC_ID(0) + { CCI_REG8(0x6001), 0x7 }, // MIPI.TINIT(0) + { CCI_REG8(0x6002), 0xd8 }, // MIPI.TINIT(1) + { CCI_REG8(0x6010), 0x0 }, // MIPI.FRAME_MODE(0) + { CCI_REG8(0x6010), 0x0 }, // MIPI.EMBEDDED_FRAME_MODE(0) + { CCI_REG8(0x6011), 0x0 }, // MIPI.DATA_ENABLE_POLARITY(0) + { CCI_REG8(0x6011), 0x0 }, // MIPI.HSYNC_POLARITY(0) + { CCI_REG8(0x6011), 0x0 }, // MIPI.VSYNC_POLARITY(0) + { CCI_REG8(0x6016), 0x0 }, // MIPI.FRAME_COUNTER(0) + { CCI_REG8(0x6017), 0x0 }, // MIPI.FRAME_COUNTER(1) + { CCI_REG8(0x6037), 0x1 }, // MIPI.LINE_COUNT_RAW8(0) + { CCI_REG8(0x6037), 0x3 }, // MIPI.LINE_COUNT_RAW10(0) + { CCI_REG8(0x6037), 0x7 }, // MIPI.LINE_COUNT_RAW12(0) + { CCI_REG8(0x6039), 0x1 }, // MIPI.LINE_COUNT_EMB(0) + { CCI_REG8(0x6018), 0x0 }, // MIPI.CCI_READ_INTERRUPT_EN(0) + { CCI_REG8(0x6018), 0x0 }, // MIPI.CCI_WRITE_INTERRUPT_EN(0) + { CCI_REG8(0x6065), 0x0 }, // MIPI.TWAKE_TIMER(0) + { CCI_REG8(0x6066), 0x0 }, // MIPI.TWAKE_TIMER(1) + { CCI_REG8(0x601c), 0x0 }, // MIPI.SKEW_CAL_EN(0) + { CCI_REG8(0x601d), 0x0 }, // MIPI.SKEW_COUNT(0) + { CCI_REG8(0x601e), 0x22 }, // MIPI.SKEW_COUNT(1) + { CCI_REG8(0x601f), 0x0 }, // MIPI.SCRAMBLING_EN(0) + { CCI_REG8(0x6003), 0x1 }, // MIPI.INIT_SKEW_EN(0) + { CCI_REG8(0x6004), 0x7a }, // MIPI.INIT_SKEW(0) + { CCI_REG8(0x6005), 0x12 }, // MIPI.INIT_SKEW(1) + { CCI_REG8(0x6006), 0x1 }, // MIPI.TX_CTRL_EN(0) + /* Processing */ + { CCI_REG8(0x4006), 0x8 }, // Processing.BSP(0) + { CCI_REG8(0x2045), 0x1 }, // Processing.CDS_RNC(0) + { CCI_REG8(0x2048), 0x1 }, // Processing.CDS_IMG(0) + { CCI_REG8(0x204b), 0x3 }, // Processing.RNC_EN(0) + { CCI_REG8(0x205b), 0x64 }, // Processing.RNC_DARK_TARGET(0) + { CCI_REG8(0x205c), 0x0 }, // Processing.RNC_DARK_TARGET(1) + { CCI_REG8(0x24dc), 0x12 }, // Defect Pixel Correction.DC_ENABLE(0) + { CCI_REG8(0x24dc), 0x10 }, // Defect Pixel Correction.DC_MODE(0) + { CCI_REG8(0x24dc), 0x0 }, // Defect Pixel Correction.DC_REPLACEMENT_VAL= UE(0) + { CCI_REG8(0x24dd), 0x0 }, // Defect Pixel Correction.DC_LIMIT_LOW(0) + { CCI_REG8(0x24de), 0x0 }, // Defect Pixel Correction.DC_LIMIT_HIGH(0) + { CCI_REG8(0x24df), 0x0 }, // Defect Pixel Correction.DC_LIMIT_HIGH_MODE= (0) + /* Illumination */ + { CCI_REG8(0x10d7), 0x1 }, // Illumination Trigger.ILLUM_EN(0) + { CCI_REG8(0x10d8), 0x2 }, // Illumination Trigger.ILLUM_POL(0) + /* Histogram */ + { CCI_REG8(0x205d), 0x0 }, // Histogram.HIST_EN(0) + { CCI_REG8(0x205e), 0x0 }, // Histogram.HIST_USAGE_RATIO(0) + { CCI_REG8(0x2063), 0x0 }, // Histogram.PIXEL_DATA_SUPP(0) + { CCI_REG8(0x2063), 0x0 }, // Histogram.PIXEL_TRANSMISSION(0) + /* TP */ + { CCI_REG8(0x2091), 0x0 }, // Test Pattern Generator.TPG_EN(0) + { CCI_REG8(0x2091), 0x0 }, // Test Pattern Generator.TPG_CONFIG(0) + /* Reduce Slew Rate - fix for defect line */ + { CCI_REG8(0x402d), 0x7b }, +}; + +static const char *const mira220_test_pattern_menu[] =3D { + "Disabled", + "Vertical Gradient", +}; + +static const int mira220_test_pattern_val[] =3D { + MIRA220_TEST_PATTERN_DISABLE, + MIRA220_TEST_PATTERN_VERTICAL_GRADIENT, +}; + +/* regulator supplies */ +static const char *const mira220_supplies[] =3D { + /* Supplies can be enabled in any order */ + "vana", /* Analog (2.8V) supply */ + "vdig", /* Digital Core (1.8V) supply */ + "vddl", /* IF (1.2V) supply */ +}; + +struct mira220 { + struct v4l2_subdev sd; + struct media_pad pad; + + enum mira220_variants variant; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; + u32 xclk_freq; + + struct regulator_bulk_data supplies[ARRAY_SIZE(mira220_supplies)]; + + struct gpio_desc *reset_gpio; + + unsigned int lanes; + unsigned int row_length; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + + struct regmap *regmap; +}; + +static inline struct mira220 *to_mira220(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct mira220, sd); +} + +static bool mira220_is_mono(struct mira220 *mira220) +{ + return mira220->variant =3D=3D MIRA220_VARIANT_MONO; +} + +/* + * The supported formats. This table contains 4 entries per format, to cov= er the + * various flip combinations in the order: + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 mira220_mbus_color_formats[] =3D { + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, +}; + +static const u32 mira220_mbus_mono_formats[] =3D { + MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y8_1X8, +}; + +#define MIRA220_FLIP_FORMAT_MASK 0x3 +static inline unsigned int mira220_flip_format_index(struct mira220 *mira2= 20, + unsigned int index) +{ + return (index & ~MIRA220_FLIP_FORMAT_MASK) | + (mira220->vflip->val ? BIT(1) : 0) | + (mira220->hflip->val ? BIT(0) : 0); +} + + +/* Power/clock management functions */ +static int mira220_power_on(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct mira220 *mira220 =3D to_mira220(sd); + int ret; + + ret =3D regulator_bulk_enable(ARRAY_SIZE(mira220_supplies), + mira220->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret =3D clk_prepare_enable(mira220->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", __func__); + goto reg_off; + } + + gpiod_set_value_cansleep(mira220->reset_gpio, 0); + + return 0; + +reg_off: + regulator_bulk_disable(ARRAY_SIZE(mira220_supplies), mira220->supplies); + return ret; +} + +static int mira220_power_off(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct mira220 *mira220 =3D to_mira220(sd); + + gpiod_set_value_cansleep(mira220->reset_gpio, 1); + clk_disable_unprepare(mira220->xclk); + regulator_bulk_disable(ARRAY_SIZE(mira220_supplies), mira220->supplies); + + return 0; +} + +static int mira220_write_start_streaming_regs(struct mira220 *mira220) +{ + int ret =3D 0; + + /* Setting master control */ + cci_write(mira220->regmap, MIRA220_IMAGER_STATE_REG, + MIRA220_IMAGER_STATE_MASTER_CONTROL, &ret); + + /* Enable continuous streaming */ + cci_write(mira220->regmap, MIRA220_IMAGER_RUN_CONT_REG, + MIRA220_IMAGER_RUN_CONT_ENABLE, &ret); + cci_write(mira220->regmap, MIRA220_IMAGER_RUN_REG, + MIRA220_IMAGER_RUN_START, &ret); + + return ret; +} + +static int mira220_write_stop_streaming_regs(struct mira220 *mira220) +{ + int ret =3D 0; + + cci_write(mira220->regmap, MIRA220_IMAGER_STATE_REG, + MIRA220_IMAGER_STATE_STOP_AT_ROW, &ret); + cci_write(mira220->regmap, MIRA220_IMAGER_RUN_REG, + MIRA220_IMAGER_RUN_STOP, &ret); + + return ret; +} + +/* + * Returns the exposure in lines. Calculation is baded on Mira220 datasheet + * Section 9.2. + */ +static u32 mira220_calc_exposure(struct mira220 *mira220, u32 height, u32 = vblank) +{ + u32 max_exposure =3D (height + vblank) - + (MIRA220_GLOB_NUM_CLK_CYCLES / mira220->row_length); + + return min(max_exposure, MIRA220_MAX_EXPOSURE); +} + +static u32 mira220_calc_min_vblank(struct mira220 *mira220) +{ + return (MIRA220_GLOB_NUM_CLK_CYCLES / mira220->row_length) + + MIRA220_MIN_VBLANK_MARGIN; +} + +static int mira220_write_exposure_reg(struct mira220 *mira220, u32 exposur= e) +{ + struct i2c_client *const client =3D v4l2_get_subdevdata(&mira220->sd); + int ret; + + ret =3D cci_write(mira220->regmap, MIRA220_EXP_TIME_REG, exposure, NULL); + if (ret) { + dev_err_ratelimited(&client->dev, + "Error setting exposure time to %d", + exposure); + return ret; + } + + return 0; +} + +/* Get bayer order based on flip setting. */ +static u32 mira220_get_format_code(struct mira220 *mira220, u32 code) +{ + unsigned int i; + + if (mira220_is_mono(mira220)) { + for (i =3D 0; i < ARRAY_SIZE(mira220_mbus_mono_formats); i++) { + if (mira220_mbus_mono_formats[i] =3D=3D code) + return mira220_mbus_mono_formats[i]; + } + + return mira220_mbus_mono_formats[0]; + } + + for (i =3D 0; i < ARRAY_SIZE(mira220_mbus_color_formats); i++) { + if (mira220_mbus_color_formats[i] =3D=3D code) + break; + } + if (i >=3D ARRAY_SIZE(mira220_mbus_color_formats)) + i =3D 0; + + i =3D mira220_flip_format_index(mira220, i); + + return mira220_mbus_color_formats[i]; +} + +static int mira220_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira220 *mira220 =3D + container_of(ctrl->handler, struct mira220, ctrl_handler); + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret =3D 0; + + state =3D v4l2_subdev_get_locked_active_state(&mira220->sd); + format =3D v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id =3D=3D V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max =3D mira220_calc_exposure(mira220, format->height, + ctrl->val); + exposure_def =3D min(exposure_max, MIRA220_DEFAULT_EXPOSURE); + ret =3D __v4l2_ctrl_modify_range(mira220->exposure, + mira220->exposure->minimum, + exposure_max, + mira220->exposure->step, + exposure_def); + if (ret) + return ret; + } + + /* Update the format code to adjust it to the new flip state. */ + if (ctrl->id =3D=3D V4L2_CID_VFLIP || ctrl->id =3D=3D V4L2_CID_HFLIP) + format->code =3D mira220_get_format_code(mira220, format->code); + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret =3D mira220_write_exposure_reg(mira220, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret =3D cci_write(mira220->regmap, MIRA220_REG_TEST_PATTERN, + mira220_test_pattern_val[ctrl->val], NULL); + break; + case V4L2_CID_HFLIP: + ret =3D cci_write(mira220->regmap, MIRA220_HFLIP_REG, + mira220->hflip->val, NULL); + break; + case V4L2_CID_VFLIP: + ret =3D cci_write(mira220->regmap, MIRA220_VFLIP_REG, + mira220->vflip->val, NULL); + break; + case V4L2_CID_VBLANK: + ret =3D cci_write(mira220->regmap, MIRA220_VBLANK_REG, ctrl->val, + NULL); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, + ctrl->val); + ret =3D -EINVAL; + break; + } + + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mira220_ctrl_ops =3D { + .s_ctrl =3D mira220_set_ctrl, +}; + +static void mira220_update_pad_format(struct mira220 *mira220, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + /* Bayer order varies with flips */ + fmt->code =3D mira220_get_format_code(mira220, code); + /* + * The mira220 sensor doesn't support binning/skipping but only + * cropping to reduce the frame size so the correct API to configure + * windowing is the .set_selection one, while format is fixed to + * the full resolution 1600x1400 one. + */ + fmt->width =3D MIRA220_PIXEL_ARRAY_WIDTH; + fmt->height =3D MIRA220_PIXEL_ARRAY_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; +} + +static int mira220_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mira220 *mira220 =3D to_mira220(sd); + struct v4l2_rect *crop; + u32 min_vblank; + int ret; + + mira220_update_pad_format(mira220, &fmt->format, fmt->format.code); + *v4l2_subdev_state_get_format(state, 0) =3D fmt->format; + + crop =3D v4l2_subdev_state_get_crop(state, 0); + crop->width =3D fmt->format.width; + crop->height =3D fmt->format.height; + crop->left =3D MIRA220_PIXEL_ARRAY_LEFT; + crop->top =3D MIRA220_PIXEL_ARRAY_TOP; + + if (fmt->which =3D=3D V4L2_SUBDEV_FORMAT_TRY) + return 0; + + /* Update vblank based on new mode. */ + min_vblank =3D mira220_calc_min_vblank(mira220); + ret =3D __v4l2_ctrl_modify_range(mira220->vblank, min_vblank, + MIRA220_MAX_VBLANK, 1, min_vblank); + if (ret) + return ret; + + return __v4l2_ctrl_s_ctrl(mira220->vblank, min_vblank); +} + +static int mira220_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mira220 *mira220 =3D to_mira220(sd); + bool mono =3D mira220_is_mono(mira220); + unsigned int num_formats; + unsigned int index; + const u32 *codes; + + num_formats =3D mono ? ARRAY_SIZE(mira220_mbus_mono_formats) : + (ARRAY_SIZE(mira220_mbus_color_formats) / 4); + + if (code->index >=3D num_formats) + return -EINVAL; + + index =3D mono ? code->index : (code->index * 4); + codes =3D mono ? mira220_mbus_mono_formats : mira220_mbus_color_formats; + code->code =3D mira220_get_format_code(mira220, codes[index]); + + return 0; +} + +static int mira220_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mira220 *mira220 =3D to_mira220(sd); + u32 code; + + if (fse->index) + return -EINVAL; + + code =3D mira220_get_format_code(mira220, fse->code); + if (fse->code !=3D code) + return -EINVAL; + + fse->min_width =3D MIRA220_PIXEL_ARRAY_WIDTH; + fse->max_width =3D fse->min_width; + fse->min_height =3D MIRA220_PIXEL_ARRAY_HEIGHT; + fse->max_height =3D fse->min_height; + + return 0; +} + +static int mira220_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt =3D { + .which =3D V4L2_SUBDEV_FORMAT_TRY, + .pad =3D 0, + .format =3D { + .code =3D MEDIA_BUS_FMT_SGRBG12_1X12, + .width =3D MIRA220_PIXEL_ARRAY_WIDTH, + .height =3D MIRA220_PIXEL_ARRAY_HEIGHT, + }, + }; + + mira220_set_pad_format(sd, state, &fmt); + + return 0; +} + +static int mira220_set_bus_config(struct mira220 *mira220) +{ + int ret =3D 0; + + cci_write(mira220->regmap, MIRA220_MIPI_LANES_REG, + mira220->lanes - 1, &ret); + + cci_write(mira220->regmap, MIRA220_MIPI_CLK_MODE_REG, + MIRA220_MIPI_CLK_CONTINUOUS, &ret); + + return ret; +} + +static int mira220_set_framefmt(struct mira220 *mira220, + struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *format; + u32 top, left; + int ret =3D 0; + + format =3D v4l2_subdev_state_get_format(state, 0); + + /* Program the image format bit order, bit depth and CSI-2 data type */ + + ret =3D cci_write(mira220->regmap, MIRA220_BIT_ORDER_REG, + MIRA220_BIT_ORDER_NORMAL, NULL); + if (ret) + return ret; + + switch (format->code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG, + MIRA220_BIT_DEPTH_8_BIT, &ret); + cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_8_BIT, &ret); + break; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG, + MIRA220_BIT_DEPTH_10_BIT, &ret); + cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_10_BIT, &ret); + + break; + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG, + MIRA220_BIT_DEPTH_12_BIT, &ret); + cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_12_BIT, &ret); + + break; + default: + ret =3D -EINVAL; + break; + } + if (ret) + return ret; + + /* Program the sensor's row length. */ + ret =3D cci_write(mira220->regmap, MIRA220_REG_ROW_LENGTH, + mira220->row_length, NULL); + if (ret) + return ret; + + /* + * Program the ROI area, centered in the sensor's pixel array. + * + * TODO: Use the crop rectangle sizes once the driver is ported to the + * new RAW camera sensor model. + */ + top =3D (MIRA220_PIXEL_ARRAY_HEIGHT - format->height) / 2; + left =3D (MIRA220_PIXEL_ARRAY_WIDTH - format->width) / 2; + + cci_write(mira220->regmap, MIRA220_VSTART_REG, top, &ret); + cci_write(mira220->regmap, MIRA220_HSTART_REG, + left & MIRA220_HSTART_MASK, &ret); + cci_write(mira220->regmap, MIRA220_VSIZE_REG, format->height, &ret); + cci_write(mira220->regmap, MIRA220_HSIZE_REG, + (format->width / 2) & MIRA220_HSIZE_MASK, &ret); + cci_write(mira220->regmap, MIRA220_MIPI_HSIZE_REG, format->width, &ret); + + return ret; +} + +static int mira220_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r =3D *v4l2_subdev_state_get_crop(state, 0); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top =3D 0; + sel->r.left =3D 0; + sel->r.width =3D MIRA220_NATIVE_WIDTH; + sel->r.height =3D MIRA220_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top =3D MIRA220_PIXEL_ARRAY_TOP; + sel->r.left =3D MIRA220_PIXEL_ARRAY_LEFT; + sel->r.width =3D MIRA220_PIXEL_ARRAY_WIDTH; + sel->r.height =3D MIRA220_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; +} + +/* OTP power on */ +static int mira220_otp_power_on(struct mira220 *mira220) +{ + int ret =3D cci_write(mira220->regmap, MIRA220_OTP_CMD_REG, + MIRA220_OTP_CMD_UP, NULL); + fsleep(100); + + return ret; +} + +/* OTP power off */ +static int mira220_otp_power_off(struct mira220 *mira220) +{ + return cci_write(mira220->regmap, MIRA220_OTP_CMD_REG, + MIRA220_OTP_CMD_DOWN, NULL); +} + +/* OTP power on */ +static int mira220_otp_read(struct mira220 *mira220, u8 addr, u8 offset, + u8 *val, int *err) +{ + u64 readback; + int ret =3D 0; + + if (err && *err) + return *err; + + cci_write(mira220->regmap, CCI_REG8(0x0086), addr, &ret); + cci_write(mira220->regmap, CCI_REG8(0x0080), 0x02, &ret); + cci_read(mira220->regmap, CCI_REG8(0x0082 + offset), &readback, &ret); + if (ret) + goto out; + + *val =3D readback & 0xFF; + + return 0; + +out: + if (err) + *err =3D ret; + return ret; +} + +/* Verify chip ID, module version, unique ID and variant */ +static int mira220_identify_module(struct mira220 *mira220) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + u8 b0, b1, b2, b3, b4, b5, b6, b7; + u32 variant; + int ret; + u8 val; + u8 id; + + ret =3D mira220_otp_power_on(mira220); + if (ret) + return ret; + + /* Log module version checks */ + mira220_otp_read(mira220, 0x0d, 0, &val, &ret); + mira220_otp_read(mira220, 0x1e, 0, &val, &ret); + if (ret) + goto err_power_off; + + /* Check unique module hardware ID version */ + ret =3D mira220_otp_read(mira220, 0x3a, 0, &id, NULL); + if (ret) + goto err_power_off; + + if (id < 1 || id > 2) { + dev_err(&client->dev, + "Read OTP 0x3a, id must be 1 or 2, but got: 0x%02x\n", id); + ret =3D -EINVAL; + goto err_power_off; + } + + /* Read Unique ID bytes sequentially using the correct 4-argument signatu= re */ + mira220_otp_read(mira220, 0x25, 0, &b0, &ret); + mira220_otp_read(mira220, 0x1e, 0, &b1, &ret); + mira220_otp_read(mira220, 0x1e, 1, &b2, &ret); + mira220_otp_read(mira220, 0x1e, 2, &b3, &ret); + mira220_otp_read(mira220, 0x1d, 0, &b4, &ret); + mira220_otp_read(mira220, 0x1d, 1, &b5, &ret); + mira220_otp_read(mira220, 0x1d, 2, &b6, &ret); + mira220_otp_read(mira220, 0x1d, 3, &b7, &ret); + if (ret) + goto err_power_off; + + dev_dbg(&client->dev, "Unique ID: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X= \n", + b7, b6, b5, b4, b3, b2, b1, b0); + + mira220_otp_read(mira220, 0x3e, 0, &b0, &ret); + mira220_otp_read(mira220, 0x3e, 1, &b1, &ret); + mira220_otp_read(mira220, 0x3e, 2, &b2, &ret); + mira220_otp_read(mira220, 0x3e, 3, &b3, &ret); + if (ret) + goto err_power_off; + + variant =3D b0 | b1 << 8 | b2 << 16 | b3 << 24; + + switch (variant) { + case MIRA220_MONO_PLAIN_PF: + case MIRA220_MONO_PLAIN_NO_PF: + case MIRA220_MONO_AR_PF: + dev_dbg(&client->dev, "Mira220 MONO variant: %x\n", variant); + mira220->variant =3D MIRA220_VARIANT_MONO; + break; + case MIRA220_RGB_PLAIN_PF: + case MIRA220_RGB_PLAIN_NO_PF: + case MIRA220_RGB_AR_PF: + dev_dbg(&client->dev, "Mira220 RGB variant: %x\n", variant); + mira220->variant =3D MIRA220_VARIANT_RGB; + break; + default: + dev_info(&client->dev, + "Unsupported mira220 variant %x; default to RGB\n", + variant); + mira220->variant =3D MIRA220_VARIANT_RGB; + break; + } + +err_power_off: + mira220_otp_power_off(mira220); + return ret; +} + +static int mira220_otp_restore(struct mira220 *mira220) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + static const u16 reg_list[] =3D { + 0x4015, 0x4016, 0x4017, 0x4018, 0x403B, 0x4040, 0x4041, 0x4042, + 0x402a, 0x4029, 0x4009 + }; + u8 val; + int ret; + + ret =3D mira220_otp_power_on(mira220); + if (ret) + return ret; + + /* Write OTP to image sensor when programmed */ + ret =3D mira220_otp_read(mira220, 0x1d, 0, &val, NULL); + if (ret < 0) { + dev_err(&client->dev, "Failed to read OTP programming status\n"); + goto out; + } + + if (val =3D=3D 0xff) { + dev_info(&client->dev, "OTP not programmed, skipping calibration\n"); + return mira220_otp_power_off(mira220); + } + + for (unsigned int i =3D 0; i < ARRAY_SIZE(reg_list); i++) { + ret =3D mira220_otp_read(mira220, i, 0, &val, NULL); + if (ret < 0) { + dev_err(&client->dev, "Failed to read OTP address %d\n", i); + goto out; + } + ret =3D cci_write(mira220->regmap, CCI_REG8(reg_list[i]), val, NULL); + if (ret < 0) { + dev_err(&client->dev, + "Failed to write register 0x%04x\n", reg_list[i]); + goto out; + } + dev_dbg(&client->dev, "OTP CALIBRATION: 0x%04x, val=3D0x%02x\n", + reg_list[i], val); + } + + ret =3D mira220_otp_read(mira220, 0x0d, 0, &val, NULL); + if (ret < 0) { + dev_err(&client->dev, "Failed to read OTP address 0x0d\n"); + goto out; + } + + ret =3D cci_write(mira220->regmap, CCI_REG8(0x403e), val, NULL); + if (ret < 0) + dev_err(&client->dev, "Failed to write register 0x403e\n"); + +out: + mira220_otp_power_off(mira220); + return ret; +} + +static int mira220_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mira220 *mira220 =3D to_mira220(sd); + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + int ret; + + ret =3D pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + /* + * Apply default values of current mode. Stop streaming before + * uploading register sequence. + */ + ret =3D mira220_write_stop_streaming_regs(mira220); + if (ret) + goto err_rpm_put; + + ret =3D cci_multi_reg_write(mira220->regmap, mira220_init_reg_list, + ARRAY_SIZE(mira220_init_reg_list), NULL); + if (ret) + goto err_rpm_put; + + ret =3D mira220_otp_restore(mira220); + if (ret) + goto err_rpm_put; + + ret =3D mira220_set_bus_config(mira220); + if (ret) + goto err_rpm_put; + + ret =3D mira220_set_framefmt(mira220, state); + if (ret) + goto err_rpm_put; + + /* Apply customized values from user */ + ret =3D __v4l2_ctrl_handler_setup(mira220->sd.ctrl_handler); + if (ret) + goto err_rpm_put; + + ret =3D mira220_write_start_streaming_regs(mira220); + if (ret) + goto err_rpm_put; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(mira220->hflip, true); + __v4l2_ctrl_grab(mira220->vflip, true); + + return 0; + +err_rpm_put: + pm_runtime_put_autosuspend(&client->dev); + return ret; +} + +static int mira220_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mira220 *mira220 =3D to_mira220(sd); + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + + mira220_write_stop_streaming_regs(mira220); + + __v4l2_ctrl_grab(mira220->hflip, false); + __v4l2_ctrl_grab(mira220->vflip, false); + + pm_runtime_put_autosuspend(&client->dev); + + return 0; +} + +static int mira220_get_regulators(struct mira220 *mira220) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + + for (unsigned int i =3D 0; i < ARRAY_SIZE(mira220_supplies); i++) + mira220->supplies[i].supply =3D mira220_supplies[i]; + + return devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(mira220_supplies), + mira220->supplies); +} + +static const struct v4l2_subdev_video_ops mira220_video_ops =3D { + .s_stream =3D v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops mira220_pad_ops =3D { + .enum_mbus_code =3D mira220_enum_mbus_code, + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D mira220_set_pad_format, + .get_selection =3D mira220_get_selection, + .enum_frame_size =3D mira220_enum_frame_size, + .enable_streams =3D mira220_enable_streams, + .disable_streams =3D mira220_disable_streams, +}; + +static const struct v4l2_subdev_ops mira220_subdev_ops =3D { + .video =3D &mira220_video_ops, + .pad =3D &mira220_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mira220_internal_ops =3D { + .init_state =3D mira220_init_state, +}; + +/* Initialize control handlers */ +static int mira220_init_controls(struct mira220 *mira220) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&mira220->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *ctrl; + u32 max_exposure =3D 0; + u32 min_vblank; + u32 hblank_val; + int ret; + + ctrl_hdlr =3D &mira220->ctrl_handler; + ret =3D v4l2_ctrl_handler_init(ctrl_hdlr, 10); + if (ret) + return ret; + + /* By default, PIXEL_RATE is read only */ + v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, V4L2_CID_PIXEL_RATE, + MIRA220_PIXEL_RATE, MIRA220_PIXEL_RATE, 1, + MIRA220_PIXEL_RATE); + + min_vblank =3D mira220_calc_min_vblank(mira220); + mira220->vblank =3D v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_VBLANK, + min_vblank, MIRA220_MAX_VBLANK, 1, + min_vblank); + + ctrl =3D v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, + 0, 0, &mira220_link_freqs[0]); + if (ctrl) + ctrl->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + /* + * Scale hblank according to the number of enabled data lanes to match + * row_length. + */ + hblank_val =3D MIRA220_LLP_1600x1400_304 * (2 / mira220->lanes) + - MIRA220_PIXEL_ARRAY_WIDTH; + ctrl =3D v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, hblank_val, + hblank_val, 1, hblank_val); + if (ctrl) + ctrl->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + /* Max exposure is determined by vblank + vsize and Tglob. */ + max_exposure =3D mira220_calc_exposure(mira220, + MIRA220_PIXEL_ARRAY_HEIGHT, + min_vblank); + + mira220->exposure =3D v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_EXPOSURE, + MIRA220_EXPOSURE_MIN, + max_exposure, 1, + MIRA220_DEFAULT_EXPOSURE); + + v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_ANALOGUE_GAIN, + MIRA220_ANALOG_GAIN_MIN, MIRA220_ANALOG_GAIN_MAX, + MIRA220_ANALOG_GAIN_STEP, + MIRA220_ANALOG_GAIN_DEFAULT); + + mira220->hflip =3D v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (mira220->hflip) + mira220->hflip->flags |=3D V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + mira220->vflip =3D v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (mira220->vflip) + mira220->vflip->flags |=3D V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mira220_test_pattern_menu) - 1, + 0, 0, mira220_test_pattern_menu); + + if (ctrl_hdlr->error) { + ret =3D ctrl_hdlr->error; + goto error; + } + + ret =3D v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret =3D v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &mira220_ctrl_ops, + &props); + if (ret) + goto error; + + mira220->sd.ctrl_handler =3D ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira= 220) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg =3D { + .bus_type =3D V4L2_MBUS_CSI2_DPHY + }; + int ret =3D 0; + + endpoint =3D fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!endpoint) { + dev_err(dev, "Endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + ret =3D -EINVAL; + dev_err(dev, "Failed to parse endpoint\n"); + goto error_out; + } + + /* Non-continuous mode not implemented. */ + if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) { + dev_warn(dev, "clock non-continuous mode not supported\n"); + ret =3D -EINVAL; + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) { + ret =3D -EINVAL; + dev_err(dev, "Unsupported number of link_frequencies: %u\n", + ep_cfg.nr_of_link_frequencies); + goto error_out; + } + + if (ep_cfg.link_frequencies[0] !=3D MIRA220_LINK_FREQ_750M) { + ret =3D -EINVAL; + dev_err(dev, "Unsupported link_frequency: %llu\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes !=3D 1 && + ep_cfg.bus.mipi_csi2.num_data_lanes !=3D 2) { + ret =3D -EINVAL; + dev_err(dev, "%u data lanes are not supported\n", + ep_cfg.bus.mipi_csi2.num_data_lanes); + goto error_out; + } + + mira220->lanes =3D ep_cfg.bus.mipi_csi2.num_data_lanes; + mira220->row_length =3D MIRA220_ROW_LENGTH_MIN * (2 / mira220->lanes); + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int mira220_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct mira220 *mira220; + int ret; + + mira220 =3D devm_kzalloc(&client->dev, sizeof(*mira220), GFP_KERNEL); + if (!mira220) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mira220->sd, client, &mira220_subdev_ops); + mira220->sd.internal_ops =3D &mira220_internal_ops; + + ret =3D mira220_parse_endpoint(dev, mira220); + if (ret) + return ret; + + mira220->regmap =3D devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(mira220->regmap)) + return dev_err_probe(dev, PTR_ERR(mira220->regmap), + "failed to initialize CCI\n"); + + /* Get system clock (xclk) */ + mira220->xclk =3D devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(mira220->xclk)) + return dev_err_probe(dev, PTR_ERR(mira220->xclk), + "failed to get xclk\n"); + + mira220->xclk_freq =3D clk_get_rate(mira220->xclk); + if (mira220->xclk_freq !=3D MIRA220_SUPPORTED_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + mira220->xclk_freq); + return -EINVAL; + } + + ret =3D mira220_get_regulators(mira220); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + mira220->reset_gpio =3D devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(mira220->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(mira220->reset_gpio), + "failed to get reset gpio\n"); + + ret =3D mira220_power_on(dev); + if (ret) + return ret; + + /* Enable runtime PM and power on the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret =3D mira220_identify_module(mira220); + if (ret) + goto error_power_off; + + ret =3D mira220_init_controls(mira220); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + mira220->sd.internal_ops =3D &mira220_internal_ops; + mira220->sd.flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE; + mira220->sd.entity.function =3D MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + mira220->pad.flags =3D MEDIA_PAD_FL_SOURCE; + + ret =3D media_entity_pads_init(&mira220->sd.entity, 1, &mira220->pad); + if (ret) { + dev_err_probe(dev, ret, "failed to init entity pads\n"); + goto error_handler_free; + } + + mira220->sd.state_lock =3D mira220->ctrl_handler.lock; + ret =3D v4l2_subdev_init_finalize(&mira220->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "subdev init error\n"); + goto error_media_entity; + } + + ret =3D v4l2_async_register_subdev_sensor(&mira220->sd); + if (ret < 0) { + dev_err_probe(dev, ret, + "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; + } + + pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&mira220->sd); +error_media_entity: + media_entity_cleanup(&mira220->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(mira220->sd.ctrl_handler); +error_power_off: + pm_runtime_disable(dev); + mira220_power_off(dev); + pm_runtime_set_suspended(dev); + return ret; +} + +static void mira220_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct mira220 *mira220 =3D to_mira220(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&mira220->sd); + media_entity_cleanup(&sd->entity); + + v4l2_ctrl_handler_free(mira220->sd.ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + mira220_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct dev_pm_ops mira220_pm_ops =3D { + SET_RUNTIME_PM_OPS(mira220_power_off, mira220_power_on, NULL) +}; + +static const struct of_device_id mira220_dt_ids[] =3D { + { .compatible =3D "ams,mira220" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira220_dt_ids); + +static struct i2c_driver mira220_i2c_driver =3D { + .driver =3D { + .name =3D "mira220", + .of_match_table =3D mira220_dt_ids, + .pm =3D pm_ptr(&mira220_pm_ops), + }, + .probe =3D mira220_probe, + .remove =3D mira220_remove, +}; + +module_i2c_driver(mira220_i2c_driver); + +MODULE_AUTHOR("Philippe Baetens "); +MODULE_DESCRIPTION("ams MIRA220 sensor driver"); +MODULE_LICENSE("GPL"); --=20 2.54.0