diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile
index 10f2efe916f2e9..f1cb7735e8bba4 100644
--- a/arch/arm/boot/dts/overlays/Makefile
+++ b/arch/arm/boot/dts/overlays/Makefile
@@ -137,6 +137,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
imx290.dtbo \
imx296.dtbo \
imx327.dtbo \
+ imx335.dtbo \
imx378.dtbo \
imx415.dtbo \
imx462.dtbo \
diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README
index d7c8782ea5927e..a5c233141d440a 100644
--- a/arch/arm/boot/dts/overlays/README
+++ b/arch/arm/boot/dts/overlays/README
@@ -2754,7 +2754,7 @@ Params: rotation Mounting rotation of the camera sensor (0 or
Compute Module (CSI0, i2c_vc, and cam0_reg).
vcm Configure a VCM focus drive on the sensor.
4lane Enable 4 CSI2 lanes. This requires a Compute
- Module (1, 3, or 4).
+ Module (1, 3, 4, or 5) or Pi 5.
Name: imx290
@@ -2763,7 +2763,7 @@ Info: Sony IMX290 camera module.
variants.
Load: dtoverlay=imx290,
Params: 4lane Enable 4 CSI2 lanes. This requires a Compute
- Module (1, 3, or 4).
+ Module (1, 3, 4, or 5) or Pi 5.
clock-frequency Sets the clock frequency to match that used on
the board.
Modules from Vision Components use 37.125MHz
@@ -2806,7 +2806,7 @@ Info: Sony IMX327 camera module.
variants.
Load: dtoverlay=imx327,
Params: 4lane Enable 4 CSI2 lanes. This requires a Compute
- Module (1, 3, or 4).
+ Module (1, 3, 4, or 5) or Pi 5.
clock-frequency Sets the clock frequency to match that used on
the board.
Modules from Vision Components use 37.125MHz
@@ -2823,6 +2823,25 @@ Params: 4lane Enable 4 CSI2 lanes. This requires a Compute
Compute Module (CSI0, i2c_vc, and cam0_reg).
+Name: imx335
+Info: Sony IMX335 camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+ variants.
+Load: dtoverlay=imx335,
+Params: orientation Sensor orientation (0 = front, 1 = rear,
+ 2 = external, default external)
+ rotation Mounting rotation of the camera sensor (0 or
+ 180, default 0)
+ media-controller Configure use of Media Controller API for
+ configuring the sensor (default on)
+ link-frequency Allowable link frequency values to use in Hz:
+ 594000000 (default), 445500000.
+ cam0 Adopt the default configuration for CAM0 on a
+ Compute Module (CSI0, i2c_vc, and cam0_reg).
+ 4lane Enable 4 CSI2 lanes. This requires a Compute
+ Module (1, 3, 4, or 5) or Pi 5.
+
+
Name: imx378
Info: Sony IMX378 camera module.
Uses Unicam 1, which is the standard camera connector on most Pi
diff --git a/arch/arm/boot/dts/overlays/imx335-overlay.dts b/arch/arm/boot/dts/overlays/imx335-overlay.dts
new file mode 100644
index 00000000000000..dfe207faecbeaa
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/imx335-overlay.dts
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX335 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include
+
+/{
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+ target = <&i2c0if>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+ clk_frag: fragment@1 {
+ target = <&cam1_clk>;
+ __overlay__ {
+ status = "okay";
+ clock-frequency = <24000000>;
+ };
+ };
+
+ fragment@2 {
+ target = <&i2c0mux>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+ reg_frag: fragment@3 {
+ target = <&cam1_reg>;
+ cam_reg: __overlay__ {
+ startup-delay-us = <200000>;
+ off-on-delay-us = <30000>;
+ regulator-min-microvolt = <2900000>;
+ regulator-max-microvolt = <2900000>;
+ };
+ };
+
+ i2c_frag: fragment@100 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ #include "imx335.dtsi"
+ };
+ };
+
+ csi_frag: fragment@101 {
+ target = <&csi1>;
+ csi: __overlay__ {
+ status = "okay";
+
+ port {
+ csi_ep: endpoint {
+ remote-endpoint = <&cam_endpoint>;
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
+ };
+
+ fragment@102 {
+ target = <&csi1>;
+ __dormant__ {
+ compatible = "brcm,bcm2835-unicam-legacy";
+ };
+ };
+
+ fragment@201 {
+ target = <&csi_ep>;
+ __dormant__ {
+ data-lanes = <1 2 3 4>;
+ };
+ };
+
+ fragment@202 {
+ target = <&cam_endpoint>;
+ __dormant__ {
+ data-lanes = <1 2 3 4>;
+ };
+ };
+
+ __overrides__ {
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <0>,"!102";
+
+ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+ <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
+ link-frequency = <&cam_endpoint>,"link-frequencies#0";
+ 4lane = <0>, "+201+202";
+ };
+};
+
+&cam_node {
+ status = "okay";
+};
+
+&cam_endpoint {
+ remote-endpoint = <&csi_ep>;
+};
diff --git a/arch/arm/boot/dts/overlays/imx335.dtsi b/arch/arm/boot/dts/overlays/imx335.dtsi
new file mode 100644
index 00000000000000..1d4b07da3b1438
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/imx335.dtsi
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Fragment that configures a Sony IMX335
+
+cam_node: imx335@1a {
+ compatible = "sony,imx335";
+ reg = <0x1a>;
+ status = "disabled";
+
+ clocks = <&cam1_clk>;
+ clock-names = "xclk";
+
+ avdd-supply = <&cam1_reg>; /* Analog 2.9v */
+ iovdd-supply = <&cam_dummy_reg>; /* Digital I/O 1.8v */
+ dvdd-supply = <&cam_dummy_reg>; /* Digital Core 1.2v */
+
+ rotation = <180>;
+ orientation = <2>;
+
+ port {
+ cam_endpoint: endpoint {
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <594000000 445500000>;
+ };
+ };
+};
diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig
index 0c2f60e19a539f..45f9948fc8f9d8 100644
--- a/arch/arm/configs/bcm2709_defconfig
+++ b/arch/arm/configs/bcm2709_defconfig
@@ -938,6 +938,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig
index 449d10b1bbd77c..8d913a6a1ee2bb 100644
--- a/arch/arm/configs/bcm2711_defconfig
+++ b/arch/arm/configs/bcm2711_defconfig
@@ -967,6 +967,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
index fa234a7e0744c4..4ac383e5d821a7 100644
--- a/arch/arm/configs/bcmrpi_defconfig
+++ b/arch/arm/configs/bcmrpi_defconfig
@@ -931,6 +931,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig
index 1b344d46806477..a40e957d6d710d 100644
--- a/arch/arm64/configs/bcm2711_defconfig
+++ b/arch/arm64/configs/bcm2711_defconfig
@@ -1016,6 +1016,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/arch/arm64/configs/bcm2711_rt_defconfig b/arch/arm64/configs/bcm2711_rt_defconfig
index cd0ebd4cc48780..9aa8084d23d7c5 100644
--- a/arch/arm64/configs/bcm2711_rt_defconfig
+++ b/arch/arm64/configs/bcm2711_rt_defconfig
@@ -1016,6 +1016,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/arch/arm64/configs/bcm2712_defconfig b/arch/arm64/configs/bcm2712_defconfig
index 9b4e554978beef..aa28b46a6aa275 100644
--- a/arch/arm64/configs/bcm2712_defconfig
+++ b/arch/arm64/configs/bcm2712_defconfig
@@ -1018,6 +1018,7 @@ CONFIG_VIDEO_IMX219=m
CONFIG_VIDEO_IMX258=m
CONFIG_VIDEO_IMX290=m
CONFIG_VIDEO_IMX296=m
+CONFIG_VIDEO_IMX335=m
CONFIG_VIDEO_IMX415=m
CONFIG_VIDEO_IMX477=m
CONFIG_VIDEO_IMX500=m
diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c
index fcfd1d851bd4aa..237dd39d5daea3 100644
--- a/drivers/media/i2c/imx335.c
+++ b/drivers/media/i2c/imx335.c
@@ -31,7 +31,7 @@
#define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d)
#define IMX335_REG_WINMODE CCI_REG8(0x3018)
#define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c)
-#define IMX335_REG_HNUM CCI_REG8(0x302e)
+#define IMX335_REG_HNUM CCI_REG16_LE(0x302e)
/* Lines per frame */
#define IMX335_REG_VMAX CCI_REG24_LE(0x3030)
@@ -56,6 +56,9 @@
#define IMX335_AGAIN_STEP 1
#define IMX335_AGAIN_DEFAULT 0
+/* Vertical flip */
+#define IMX335_REG_VREVERSE CCI_REG8(0x304f)
+
#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148)
#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c)
@@ -155,6 +158,8 @@ static const char * const imx335_supply_name[] = {
* @vblank_max: Maximum vertical blanking in lines
* @pclk: Sensor pixel clock
* @reg_list: Register list for sensor mode
+ * @vflip_normal: Register list vflip (normal readout)
+ * @vflip_inverted: Register list vflip (inverted readout)
*/
struct imx335_mode {
u32 width;
@@ -166,6 +171,8 @@ struct imx335_mode {
u32 vblank_max;
u64 pclk;
struct imx335_reg_list reg_list;
+ struct imx335_reg_list vflip_normal;
+ struct imx335_reg_list vflip_inverted;
};
/**
@@ -183,6 +190,7 @@ struct imx335_mode {
* @pclk_ctrl: Pointer to pixel clock control
* @hblank_ctrl: Pointer to horizontal blanking control
* @vblank_ctrl: Pointer to vertical blanking control
+ * @vflip: Pointer to vertical flip control
* @exp_ctrl: Pointer to exposure control
* @again_ctrl: Pointer to analog gain control
* @vblank: Vertical blanking in lines
@@ -207,6 +215,7 @@ struct imx335 {
struct v4l2_ctrl *pclk_ctrl;
struct v4l2_ctrl *hblank_ctrl;
struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *vflip;
struct {
struct v4l2_ctrl *exp_ctrl;
struct v4l2_ctrl *again_ctrl;
@@ -252,14 +261,13 @@ static const int imx335_tpg_val[] = {
};
/* Sensor mode registers */
-static const struct cci_reg_sequence mode_2592x1940_regs[] = {
+static const struct cci_reg_sequence mode_2592x1944_regs[] = {
{ IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
{ IMX335_REG_MASTER_MODE, 0x00 },
{ IMX335_REG_WINMODE, 0x04 },
{ IMX335_REG_HTRIMMING_START, 48 },
{ IMX335_REG_HNUM, 2592 },
{ IMX335_REG_Y_OUT_SIZE, 1944 },
- { IMX335_REG_AREA3_ST_ADR_1, 176 },
{ IMX335_REG_AREA3_WIDTH_1, 3928 },
{ IMX335_REG_OPB_SIZE_V, 0 },
{ IMX335_REG_XVS_XHS_DRV, 0x00 },
@@ -333,6 +341,26 @@ static const struct cci_reg_sequence mode_2592x1940_regs[] = {
{ CCI_REG8(0x3a00), 0x00 },
};
+static const struct cci_reg_sequence mode_2592x1944_vflip_normal[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 176 },
+
+ /* Undocumented V-Flip related registers on Page 55 of datasheet. */
+ { CCI_REG8(0x3081), 0x02, },
+ { CCI_REG8(0x3083), 0x02, },
+ { CCI_REG16_LE(0x30b6), 0x00 },
+ { CCI_REG16_LE(0x3116), 0x08 },
+};
+
+static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 4112 },
+
+ /* Undocumented V-Flip related registers on Page 55 of datasheet. */
+ { CCI_REG8(0x3081), 0xfe, },
+ { CCI_REG8(0x3083), 0xfe, },
+ { CCI_REG16_LE(0x30b6), 0x1fa },
+ { CCI_REG16_LE(0x3116), 0x002 },
+};
+
static const struct cci_reg_sequence raw10_framefmt_regs[] = {
{ IMX335_REG_ADBIT, 0x00 },
{ IMX335_REG_MDBIT, 0x00 },
@@ -416,8 +444,16 @@ static const struct imx335_mode supported_mode = {
.vblank_max = 133060,
.pclk = 396000000,
.reg_list = {
- .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs),
- .regs = mode_2592x1940_regs,
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs),
+ .regs = mode_2592x1944_regs,
+ },
+ .vflip_normal = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal),
+ .regs = mode_2592x1944_vflip_normal,
+ },
+ .vflip_inverted = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted),
+ .regs = mode_2592x1944_vflip_inverted,
},
};
@@ -492,6 +528,26 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
return ret;
}
+static int imx335_update_vertical_flip(struct imx335 *imx335, u32 vflip)
+{
+ int ret = 0;
+
+ if (vflip)
+ cci_multi_reg_write(imx335->cci,
+ imx335->cur_mode->vflip_inverted.regs,
+ imx335->cur_mode->vflip_inverted.num_of_regs,
+ &ret);
+ else
+ cci_multi_reg_write(imx335->cci,
+ imx335->cur_mode->vflip_normal.regs,
+ imx335->cur_mode->vflip_normal.num_of_regs,
+ &ret);
+ if (ret)
+ return ret;
+
+ return cci_write(imx335->cci, IMX335_REG_VREVERSE, vflip, NULL);
+}
+
static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
{
int ret = 0;
@@ -559,12 +615,12 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
imx335->vblank,
imx335->vblank + imx335->cur_mode->height);
- return __v4l2_ctrl_modify_range(imx335->exp_ctrl,
- IMX335_EXPOSURE_MIN,
- imx335->vblank +
- imx335->cur_mode->height -
- IMX335_EXPOSURE_OFFSET,
- 1, IMX335_EXPOSURE_DEFAULT);
+ __v4l2_ctrl_modify_range(imx335->exp_ctrl,
+ IMX335_EXPOSURE_MIN,
+ imx335->vblank +
+ imx335->cur_mode->height -
+ IMX335_EXPOSURE_OFFSET,
+ 1, IMX335_EXPOSURE_DEFAULT);
}
/*
@@ -575,6 +631,13 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
return 0;
switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ exposure = imx335->exp_ctrl->val;
+ analog_gain = imx335->again_ctrl->val;
+
+ ret = imx335_update_exp_gain(imx335, exposure, analog_gain);
+
+ break;
case V4L2_CID_EXPOSURE:
exposure = ctrl->val;
analog_gain = imx335->again_ctrl->val;
@@ -584,6 +647,10 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
ret = imx335_update_exp_gain(imx335, exposure, analog_gain);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = imx335_update_vertical_flip(imx335, ctrl->val);
+
break;
case V4L2_CID_TEST_PATTERN:
ret = imx335_update_test_pattern(imx335, ctrl->val);
@@ -1166,7 +1233,7 @@ static int imx335_init_controls(struct imx335 *imx335)
return ret;
/* v4l2_fwnode_device_properties can add two more controls */
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
@@ -1201,6 +1268,13 @@ static int imx335_init_controls(struct imx335 *imx335)
v4l2_ctrl_cluster(2, &imx335->exp_ctrl);
+ imx335->vflip = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_VFLIP,
+ 0, 1, 1, 0);
+ if (imx335->vflip)
+ imx335->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
&imx335_ctrl_ops,
V4L2_CID_VBLANK,