diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 964687b7a5c712..4007bbbdac3a1b 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -3718,6 +3718,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or clk-continuous Switch to continuous mode on the CSI clock lane, which increases the maximum frame rate slightly. Appears not to work on Pi3. + trigger-mode Enable external trigger mode (0 = normal, + 1 = triggered). In this mode, the sensor outputs + a frame only when triggered by a rising edge + on the FSIN input pin. Name: papirus diff --git a/arch/arm/boot/dts/overlays/ov9281-overlay.dts b/arch/arm/boot/dts/overlays/ov9281-overlay.dts index 82dd992fdb7e90..f7da0869044573 100644 --- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts +++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts @@ -96,6 +96,7 @@ <®_frag>, "target:0=",<&cam0_reg>; arducam = <0>, "+6"; clk-continuous = <0>, "-7-8"; + trigger-mode = <&cam_node>, "trigger-mode:0"; }; }; diff --git a/arch/arm/boot/dts/overlays/ov9281.dtsi b/arch/arm/boot/dts/overlays/ov9281.dtsi index 321a2eb8c8f2fc..80f9da98ee8a1a 100644 --- a/arch/arm/boot/dts/overlays/ov9281.dtsi +++ b/arch/arm/boot/dts/overlays/ov9281.dtsi @@ -14,6 +14,7 @@ cam_node: ov9281@60 { rotation = <0>; orientation = <2>; + trigger-mode = <0>; port { cam_endpoint: endpoint { diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index cff15da3436b35..eb2f404d51c786 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -65,6 +65,14 @@ #define OV9282_REG_MIPI_CTRL00 0x4800 #define OV9282_GATED_CLOCK BIT(5) +/* Trigger mode registers */ +#define OV9282_REG_POWER_CTRL 0x4F00 +#define OV9282_REG_LOW_POWER_MODE_CTRL 0x3030 +#define OV9282_REG_NUM_FRAME_ON_TRIG 0x303F +#define OV9282_REG_SLEEP_PERIOD_CTRL0 0x302C +#define OV9282_REG_SLEEP_PERIOD_CTRL3 0x302F +#define OV9282_REG_TIMING_23 0x3823 + /* Input clock rate */ #define OV9282_INCLK_RATE 24000000 @@ -187,6 +195,7 @@ struct ov9282 { const struct ov9282_mode *cur_mode; u32 code; struct mutex mutex; + int trigger_mode; }; static const s64 link_freq[] = { @@ -947,6 +956,65 @@ static int ov9282_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +/** + * ov9282_apply_trigger_config() - Configure sensor for FSIN external trigger mode + * @ov9282: pointer to ov9282 device + * + * Return: 0 on success, error code otherwise. + */ +static int ov9282_apply_trigger_config(struct ov9282 *ov9282) +{ + int ret; + + ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, 1, OV9282_MODE_STANDBY); + if (ret) { + return ret; + } + + /* Low power mode */ + ret = ov9282_write_reg(ov9282, OV9282_REG_POWER_CTRL, 1, 0x01); + if (ret) { + return ret; + } + + /* External trigger snapshot */ + ret = ov9282_write_reg(ov9282, OV9282_REG_LOW_POWER_MODE_CTRL, 1, 0x04); + if (ret) { + return ret; + } + + /* 1 frame per trigger */ + ov9282_write_reg(ov9282, OV9282_REG_NUM_FRAME_ON_TRIG, 1, 0x01); + if (ret) { + return ret; + } + + ov9282_write_reg(ov9282, OV9282_REG_SLEEP_PERIOD_CTRL0, 1, 0x00); + if (ret) { + return ret; + } + + ov9282_write_reg(ov9282, OV9282_REG_SLEEP_PERIOD_CTRL3, 1, 0x7F); + if (ret) { + return ret; + } + + /* No auto wake */ + ov9282_write_reg(ov9282, OV9282_REG_TIMING_23, 1, 0x00); + if (ret) { + return ret; + } + + /* stay standby mode and wait for trigger signal */ + ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, + 1, OV9282_MODE_STANDBY); + if (ret) { + return ret; + } + + return 0; +} + /** * ov9282_start_streaming() - Start sensor stream * @ov9282: pointer to ov9282 device @@ -970,7 +1038,7 @@ static int ov9282_start_streaming(struct ov9282 *ov9282) /* Write common registers */ ret = ov9282_write_regs(ov9282, common_regs_list.regs, - common_regs_list.num_of_regs); + common_regs_list.num_of_regs); if (ret) { dev_err(ov9282->dev, "fail to write common registers"); return ret; @@ -992,15 +1060,25 @@ static int ov9282_start_streaming(struct ov9282 *ov9282) } /* Setup handler will write actual exposure and gain */ - ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler); + ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler); if (ret) { dev_err(ov9282->dev, "fail to setup handler"); return ret; } + if (ov9282->trigger_mode > 0) { + /* Configure FSIN external trigger mode */ + ret = ov9282_apply_trigger_config(ov9282); + if (ret) { + dev_err(ov9282->dev, "failed to config external trigger mode"); + return ret; + } + return 0; + } + /* Start streaming */ ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, - 1, OV9282_MODE_STREAMING); + 1, OV9282_MODE_STREAMING); if (ret) { dev_err(ov9282->dev, "fail to start streaming"); return ret; @@ -1392,6 +1470,7 @@ static int ov9282_probe(struct i2c_client *client) { struct ov9282 *ov9282; int ret; + u32 trig_mod; ov9282 = devm_kzalloc(&client->dev, sizeof(*ov9282), GFP_KERNEL); if (!ov9282) @@ -1431,6 +1510,9 @@ static int ov9282_probe(struct i2c_client *client) ov9282->code = MEDIA_BUS_FMT_Y10_1X10; ov9282->vblank = ov9282->cur_mode->vblank; + ret = of_property_read_u32(client->dev.of_node, "trigger-mode", &trig_mod); + ov9282->trigger_mode = (ret == 0) ? trig_mod : -1; + ret = ov9282_init_controls(ov9282); if (ret) { dev_err(ov9282->dev, "failed to init controls: %d", ret);