On Tue Oct 14 19:40:24 2025 +0200, Hans de Goede wrote:
> Remove the fixed mode list and add cropping support. The main reason for
> doing this is to allow libcamera to select 1292x812 instead of 1280x800
> so that after the extra border which the CPU debayer code needs libcamera
> can output 1280x720 instead of 1276x720.
> 
> This in turn allows google-meet to use 720p instead of it falling back
> to a pretty bad 360p.
> 
> This has been tested on a Dell XPS 9320, with both libcamera as well as
> with Intel's out-of-tree psys driver + proprietary userspace stack.
> 
> Libcamera asks for 1292x812 where as the Intel stack asks for 1280x800
> and neither stack explicitly sets the crop-window. Hence the need for
> ov01a10_set_format() to adjust the crop-window if necessary.
> 
> Note the differentiating between pattern_size and border_size is done in
> preparation for adding support for the monochrome OV01A1B model where
> coordinates still need to be aligned to a multiple of 2, but there will
> be no need for a border (border_size=0).
> 
> Link: https://bugzilla.redhat.com/show_bug.cgi?id=2337593
> Signed-off-by: Hans de Goede <[email protected]>
> Tested-by: Mehdi Djait <[email protected]> # Dell XPS 9315
> Reviewed-by: Mehdi Djait <[email protected]>
> Signed-off-by: Sakari Ailus <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/i2c/ov01a10.c | 348 +++++++++++++++++++++++++++-----------------
 1 file changed, 212 insertions(+), 136 deletions(-)

---

diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c
index 3eb6445b8f00..349fd3d06df5 100644
--- a/drivers/media/i2c/ov01a10.c
+++ b/drivers/media/i2c/ov01a10.c
@@ -40,7 +40,6 @@
 #define OV01A10_DEFAULT_HEIGHT         800
 
 /* vertical and horizontal timings */
-#define OV01A10_REG_VTS                        CCI_REG16(0x380e)
 #define OV01A10_VTS_DEF                        0x0700
 #define OV01A10_VTS_MIN                        0x0380
 #define OV01A10_VTS_MAX                        0xffff
@@ -68,19 +67,26 @@
 #define OV01A10_DGTL_GAIN_STEP         1
 #define OV01A10_DGTL_GAIN_DEFAULT      1024
 
-/* test pattern control */
-#define OV01A10_REG_TEST_PATTERN       CCI_REG8(0x4503)
-#define OV01A10_TEST_PATTERN_ENABLE    BIT(7)
-#define OV01A10_LINK_FREQ_400MHZ_INDEX 0
+/* timing control */
+#define OV01A10_REG_X_ADDR_START       CCI_REG16(0x3800)
+#define OV01A10_REG_Y_ADDR_START       CCI_REG16(0x3802)
+#define OV01A10_REG_X_ADDR_END         CCI_REG16(0x3804)
+#define OV01A10_REG_Y_ADDR_END         CCI_REG16(0x3806)
+#define OV01A10_REG_X_OUTPUT_SIZE      CCI_REG16(0x3808)
+#define OV01A10_REG_Y_OUTPUT_SIZE      CCI_REG16(0x380a)
+#define OV01A10_REG_HTS                        CCI_REG16(0x380c) /* in units 
of 2 pixels */
+#define OV01A10_REG_VTS                        CCI_REG16(0x380e)
+#define OV01A10_REG_X_WIN              CCI_REG16(0x3810)
+#define OV01A10_REG_Y_WIN              CCI_REG16(0x3812)
 
 /* flip and mirror control */
 #define OV01A10_REG_FORMAT1            CCI_REG8(0x3820)
 #define OV01A10_VFLIP_MASK             BIT(4)
 #define OV01A10_HFLIP_MASK             BIT(3)
 
-/* window offset */
-#define OV01A10_REG_X_WIN              CCI_REG16(0x3810)
-#define OV01A10_REG_Y_WIN              CCI_REG16(0x3812)
+/* test pattern control */
+#define OV01A10_REG_TEST_PATTERN       CCI_REG8(0x4503)
+#define OV01A10_TEST_PATTERN_ENABLE    BIT(7)
 
 /*
  * The native ov01a10 bayer-pattern is GBRG, but there was a driver bug 
enabling
@@ -90,6 +96,7 @@
  * when hflip is *disabled*.
  */
 #define OV01A10_MEDIA_BUS_FMT          MEDIA_BUS_FMT_SBGGR10_1X10
+#define OV01A10_BAYER_PATTERN_SIZE     2 /* 2x2 */
 
 struct ov01a10_reg_list {
        u32 num_of_regs;
@@ -100,17 +107,6 @@ struct ov01a10_link_freq_config {
        const struct ov01a10_reg_list reg_list;
 };
 
-struct ov01a10_mode {
-       u32 width;
-       u32 height;
-       u32 hts;
-       u32 vts_def;
-       u32 vts_min;
-       u32 link_freq_index;
-
-       const struct ov01a10_reg_list reg_list;
-};
-
 static const struct reg_sequence mipi_data_rate_720mbps[] = {
        {0x0103, 0x01},
        {0x0302, 0x00},
@@ -127,7 +123,7 @@ static const struct reg_sequence mipi_data_rate_720mbps[] = 
{
        {0x0325, 0x68},
 };
 
-static const struct reg_sequence sensor_1280x800_setting[] = {
+static const struct reg_sequence ov01a10_global_setting[] = {
        {0x3002, 0xa1},
        {0x301e, 0xf0},
        {0x3022, 0x01},
@@ -179,26 +175,6 @@ static const struct reg_sequence sensor_1280x800_setting[] 
= {
        {0x37e4, 0x04},
        {0x37e5, 0x03},
        {0x37e6, 0x04},
-       {0x3800, 0x00},
-       {0x3801, 0x00},
-       {0x3802, 0x00},
-       {0x3803, 0x00},
-       {0x3804, 0x05},
-       {0x3805, 0x0f},
-       {0x3806, 0x03},
-       {0x3807, 0x2f},
-       {0x3808, 0x05},
-       {0x3809, 0x00},
-       {0x380a, 0x03},
-       {0x380b, 0x20},
-       {0x380c, 0x02},
-       {0x380d, 0xe8},
-       {0x380e, 0x07},
-       {0x380f, 0x00},
-       {0x3810, 0x00},
-       {0x3811, 0x09},
-       {0x3812, 0x00},
-       {0x3813, 0x08},
        {0x3814, 0x01},
        {0x3815, 0x01},
        {0x3816, 0x01},
@@ -260,7 +236,7 @@ static const s64 link_freq_menu_items[] = {
 };
 
 static const struct ov01a10_link_freq_config link_freq_configs[] = {
-       [OV01A10_LINK_FREQ_400MHZ_INDEX] = {
+       {
                .reg_list = {
                        .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps),
                        .regs = mipi_data_rate_720mbps,
@@ -268,19 +244,11 @@ static const struct ov01a10_link_freq_config 
link_freq_configs[] = {
        },
 };
 
-static const struct ov01a10_mode supported_modes[] = {
-       {
-               .width = OV01A10_DEFAULT_WIDTH,
-               .height = OV01A10_DEFAULT_HEIGHT,
-               .hts = OV01A10_HTS_DEF,
-               .vts_def = OV01A10_VTS_DEF,
-               .vts_min = OV01A10_VTS_MIN,
-               .reg_list = {
-                       .num_of_regs = ARRAY_SIZE(sensor_1280x800_setting),
-                       .regs = sensor_1280x800_setting,
-               },
-               .link_freq_index = OV01A10_LINK_FREQ_400MHZ_INDEX,
-       },
+static const struct v4l2_rect ov01a10_default_crop = {
+       .left = (OV01A10_NATIVE_WIDTH - OV01A10_DEFAULT_WIDTH) / 2,
+       .top = (OV01A10_NATIVE_HEIGHT - OV01A10_DEFAULT_HEIGHT) / 2,
+       .width = OV01A10_DEFAULT_WIDTH,
+       .height = OV01A10_DEFAULT_HEIGHT,
 };
 
 static const char * const ov01a10_supply_names[] = {
@@ -303,7 +271,6 @@ struct ov01a10 {
        struct v4l2_ctrl *hblank;
        struct v4l2_ctrl *exposure;
 
-       const struct ov01a10_mode *cur_mode;
        u32 link_freq_index;
 
        struct clk *clk;
@@ -317,6 +284,22 @@ static inline struct ov01a10 *to_ov01a10(struct 
v4l2_subdev *subdev)
        return container_of(subdev, struct ov01a10, sd);
 }
 
+static struct v4l2_mbus_framefmt *ov01a10_get_active_format(struct ov01a10 
*ov01a10)
+{
+       struct v4l2_subdev_state *active_state =
+               v4l2_subdev_get_locked_active_state(&ov01a10->sd);
+
+       return v4l2_subdev_state_get_format(active_state, 0);
+}
+
+static struct v4l2_rect *ov01a10_get_active_crop(struct ov01a10 *ov01a10)
+{
+       struct v4l2_subdev_state *active_state =
+               v4l2_subdev_get_locked_active_state(&ov01a10->sd);
+
+       return v4l2_subdev_state_get_crop(active_state, 0);
+}
+
 static int ov01a10_update_digital_gain(struct ov01a10 *ov01a10, u32 d_gain)
 {
        u32 real = d_gain << 6;
@@ -339,13 +322,16 @@ static int ov01a10_test_pattern(struct ov01a10 *ov01a10, 
u32 pattern)
                         NULL);
 }
 
-/* for vflip and hflip, use 0x9 as window offset to keep the bayer */
 static int ov01a10_set_hflip(struct ov01a10 *ov01a10, u32 hflip)
 {
+       struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10);
        u32 val, offset;
        int ret = 0;
 
-       offset = hflip ? 0x8 : 0x9;
+       offset = crop->left;
+       if (!hflip)
+               offset++;
+
        val = hflip ? 0 : FIELD_PREP(OV01A10_HFLIP_MASK, 0x1);
 
        cci_write(ov01a10->regmap, OV01A10_REG_X_WIN, offset, &ret);
@@ -357,10 +343,14 @@ static int ov01a10_set_hflip(struct ov01a10 *ov01a10, u32 
hflip)
 
 static int ov01a10_set_vflip(struct ov01a10 *ov01a10, u32 vflip)
 {
+       struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10);
        u32 val, offset;
        int ret = 0;
 
-       offset = vflip ? 0x9 : 0x8;
+       offset = crop->top;
+       if (vflip)
+               offset++;
+
        val = vflip ? FIELD_PREP(OV01A10_VFLIP_MASK, 0x1) : 0;
 
        cci_write(ov01a10->regmap, OV01A10_REG_Y_WIN, offset, &ret);
@@ -374,12 +364,13 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct ov01a10 *ov01a10 = container_of(ctrl->handler,
                                               struct ov01a10, ctrl_handler);
+       struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10);
        s64 exposure_max;
        int ret = 0;
 
        if (ctrl->id == V4L2_CID_VBLANK) {
-               exposure_max = ov01a10->cur_mode->height + ctrl->val -
-                       OV01A10_EXPOSURE_MAX_MARGIN;
+               exposure_max = fmt->height + ctrl->val -
+                              OV01A10_EXPOSURE_MAX_MARGIN;
                __v4l2_ctrl_modify_range(ov01a10->exposure,
                                         ov01a10->exposure->minimum,
                                         exposure_max, ov01a10->exposure->step,
@@ -406,7 +397,7 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl)
 
        case V4L2_CID_VBLANK:
                ret = cci_write(ov01a10->regmap, OV01A10_REG_VTS,
-                               ov01a10->cur_mode->height + ctrl->val, NULL);
+                               fmt->height + ctrl->val, NULL);
                break;
 
        case V4L2_CID_TEST_PATTERN:
@@ -440,7 +431,6 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
        struct v4l2_fwnode_device_properties props;
        u32 vblank_min, vblank_max, vblank_default;
        struct v4l2_ctrl_handler *ctrl_hdlr;
-       const struct ov01a10_mode *cur_mode;
        s64 exposure_max, h_blank;
        int ret = 0;
 
@@ -453,8 +443,6 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
        if (ret)
                return ret;
 
-       cur_mode = ov01a10->cur_mode;
-
        ov01a10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
                                                    &ov01a10_ctrl_ops,
                                                    V4L2_CID_LINK_FREQ,
@@ -465,14 +453,14 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
                                                V4L2_CID_PIXEL_RATE, 0,
                                                OV01A10_SCLK, 1, OV01A10_SCLK);
 
-       vblank_min = cur_mode->vts_min - cur_mode->height;
-       vblank_max = OV01A10_VTS_MAX - cur_mode->height;
-       vblank_default = cur_mode->vts_def - cur_mode->height;
+       vblank_min = OV01A10_VTS_MIN - OV01A10_DEFAULT_HEIGHT;
+       vblank_max = OV01A10_VTS_MAX - OV01A10_DEFAULT_HEIGHT;
+       vblank_default = OV01A10_VTS_DEF - OV01A10_DEFAULT_HEIGHT;
        ov01a10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
                                            V4L2_CID_VBLANK, vblank_min,
                                            vblank_max, 1, vblank_default);
 
-       h_blank = cur_mode->hts - cur_mode->width;
+       h_blank = OV01A10_HTS_DEF - OV01A10_DEFAULT_WIDTH;
        ov01a10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
                                            V4L2_CID_HBLANK, h_blank, h_blank,
                                            1, h_blank);
@@ -484,7 +472,7 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
                          OV01A10_DGTL_GAIN_MIN, OV01A10_DGTL_GAIN_MAX,
                          OV01A10_DGTL_GAIN_STEP, OV01A10_DGTL_GAIN_DEFAULT);
 
-       exposure_max = cur_mode->vts_def - OV01A10_EXPOSURE_MAX_MARGIN;
+       exposure_max = OV01A10_VTS_DEF - OV01A10_EXPOSURE_MAX_MARGIN;
        ov01a10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
                                              V4L2_CID_EXPOSURE,
                                              OV01A10_EXPOSURE_MIN,
@@ -524,24 +512,48 @@ fail:
        return ret;
 }
 
-static void ov01a10_update_pad_format(const struct ov01a10_mode *mode,
-                                     struct v4l2_mbus_framefmt *fmt)
+static void ov01a10_fill_format(struct v4l2_mbus_framefmt *fmt,
+                               unsigned int width, unsigned int height)
 {
-       fmt->width = mode->width;
-       fmt->height = mode->height;
+       memset(fmt, 0, sizeof(*fmt));
+       fmt->width = width;
+       fmt->height = height;
        fmt->code = OV01A10_MEDIA_BUS_FMT;
        fmt->field = V4L2_FIELD_NONE;
        fmt->colorspace = V4L2_COLORSPACE_RAW;
 }
 
+static int ov01a10_set_mode(struct ov01a10 *ov01a10)
+{
+       struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10);
+       int ret = 0;
+
+       cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_START, 0, &ret);
+       cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_START, 0, &ret);
+       cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_END,
+                 OV01A10_NATIVE_WIDTH - 1, &ret);
+       cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_END,
+                 OV01A10_NATIVE_HEIGHT - 1, &ret);
+       cci_write(ov01a10->regmap, OV01A10_REG_X_OUTPUT_SIZE,
+                 fmt->width, &ret);
+       cci_write(ov01a10->regmap, OV01A10_REG_Y_OUTPUT_SIZE,
+                 fmt->height, &ret);
+       /* HTS register is in units of 2 pixels */
+       cci_write(ov01a10->regmap, OV01A10_REG_HTS,
+                 OV01A10_HTS_DEF / 2, &ret);
+       /* OV01A10_REG_VTS is set by vblank control */
+       /* OV01A10_REG_X_WIN is set by hlip control */
+       /* OV01A10_REG_Y_WIN is set by vflip control */
+
+       return ret;
+}
+
 static int ov01a10_start_streaming(struct ov01a10 *ov01a10)
 {
        const struct ov01a10_reg_list *reg_list;
-       int link_freq_index;
-       int ret = 0;
+       int ret;
 
-       link_freq_index = ov01a10->cur_mode->link_freq_index;
-       reg_list = &link_freq_configs[link_freq_index].reg_list;
+       reg_list = &link_freq_configs[ov01a10->link_freq_index].reg_list;
        ret = regmap_multi_reg_write(ov01a10->regmap, reg_list->regs,
                                     reg_list->num_of_regs);
        if (ret) {
@@ -549,9 +561,14 @@ static int ov01a10_start_streaming(struct ov01a10 *ov01a10)
                return ret;
        }
 
-       reg_list = &ov01a10->cur_mode->reg_list;
-       ret = regmap_multi_reg_write(ov01a10->regmap, reg_list->regs,
-                                    reg_list->num_of_regs);
+       ret = regmap_multi_reg_write(ov01a10->regmap, ov01a10_global_setting,
+                                    ARRAY_SIZE(ov01a10_global_setting));
+       if (ret) {
+               dev_err(ov01a10->dev, "failed to initialize sensor\n");
+               return ret;
+       }
+
+       ret = ov01a10_set_mode(ov01a10);
        if (ret) {
                dev_err(ov01a10->dev, "failed to set mode\n");
                return ret;
@@ -600,54 +617,64 @@ unlock:
        return ret;
 }
 
+static void ov01a10_update_blank_ctrls(struct ov01a10 *ov01a10,
+                                      unsigned int width, unsigned int height)
+{
+       s32 hblank, vblank_def;
+
+       vblank_def = OV01A10_VTS_DEF - height;
+       __v4l2_ctrl_modify_range(ov01a10->vblank,
+                                OV01A10_VTS_MIN - height,
+                                OV01A10_VTS_MAX - height, 1,
+                                vblank_def);
+       __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def);
+
+       hblank = OV01A10_HTS_DEF - width;
+       __v4l2_ctrl_modify_range(ov01a10->hblank, hblank, hblank, 1, hblank);
+}
+
 static int ov01a10_set_format(struct v4l2_subdev *sd,
                              struct v4l2_subdev_state *sd_state,
                              struct v4l2_subdev_format *fmt)
 {
+       struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad);
+       const int pattern_size = OV01A10_BAYER_PATTERN_SIZE;
+       const int border_size = OV01A10_BAYER_PATTERN_SIZE;
        struct ov01a10 *ov01a10 = to_ov01a10(sd);
-       const struct ov01a10_mode *mode;
-       struct v4l2_mbus_framefmt *format;
-       s32 vblank_def, h_blank;
-
-       mode = v4l2_find_nearest_size(supported_modes,
-                                     ARRAY_SIZE(supported_modes), width,
-                                     height, fmt->format.width,
-                                     fmt->format.height);
-
-       ov01a10_update_pad_format(mode, &fmt->format);
-
-       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
-               ov01a10->cur_mode = mode;
-
-               vblank_def = mode->vts_def - mode->height;
-               __v4l2_ctrl_modify_range(ov01a10->vblank,
-                                        mode->vts_min - mode->height,
-                                        OV01A10_VTS_MAX - mode->height, 1,
-                                        vblank_def);
-               __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def);
-               h_blank = mode->hts - mode->width;
-               __v4l2_ctrl_modify_range(ov01a10->hblank, h_blank, h_blank, 1,
-                                        h_blank);
+       unsigned int width, height;
+
+       width = clamp_val(ALIGN(fmt->format.width, pattern_size),
+                         pattern_size,
+                         OV01A10_NATIVE_WIDTH - 2 * border_size);
+       height = clamp_val(ALIGN(fmt->format.height, pattern_size),
+                          pattern_size,
+                          OV01A10_NATIVE_HEIGHT - 2 * border_size);
+
+       /* Center image for userspace which does not set the crop first */
+       if (width != crop->width || height != crop->height) {
+               crop->left = ALIGN((OV01A10_NATIVE_WIDTH - width) / 2,
+                                  pattern_size);
+               crop->top = ALIGN((OV01A10_NATIVE_HEIGHT - height) / 2,
+                                 pattern_size);
+               crop->width = width;
+               crop->height = height;
        }
 
-       format = v4l2_subdev_state_get_format(sd_state, fmt->pad);
-       *format = fmt->format;
+       ov01a10_fill_format(&fmt->format, width, height);
+       *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
+
+       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+               ov01a10_update_blank_ctrls(ov01a10, width, height);
 
        return 0;
 }
 
 static int ov01a10_init_state(struct v4l2_subdev *sd,
-                             struct v4l2_subdev_state *state)
+                             struct v4l2_subdev_state *sd_state)
 {
-       struct v4l2_subdev_format fmt = {
-               .which = V4L2_SUBDEV_FORMAT_TRY,
-               .format = {
-                       .width = OV01A10_DEFAULT_WIDTH,
-                       .height = OV01A10_DEFAULT_HEIGHT,
-               },
-       };
-
-       ov01a10_set_format(sd, state, &fmt);
+       *v4l2_subdev_state_get_crop(sd_state, 0) = ov01a10_default_crop;
+       ov01a10_fill_format(v4l2_subdev_state_get_format(sd_state, 0),
+                           OV01A10_DEFAULT_WIDTH, OV01A10_DEFAULT_HEIGHT);
 
        return 0;
 }
@@ -668,14 +695,16 @@ static int ov01a10_enum_frame_size(struct v4l2_subdev *sd,
                                   struct v4l2_subdev_state *sd_state,
                                   struct v4l2_subdev_frame_size_enum *fse)
 {
-       if (fse->index >= ARRAY_SIZE(supported_modes) ||
-           fse->code != OV01A10_MEDIA_BUS_FMT)
+       const int pattern_size = OV01A10_BAYER_PATTERN_SIZE;
+       const int border_size = OV01A10_BAYER_PATTERN_SIZE;
+
+       if (fse->index)
                return -EINVAL;
 
-       fse->min_width = supported_modes[fse->index].width;
-       fse->max_width = fse->min_width;
-       fse->min_height = supported_modes[fse->index].height;
-       fse->max_height = fse->min_height;
+       fse->min_width = pattern_size;
+       fse->max_width = OV01A10_NATIVE_WIDTH - 2 * border_size;
+       fse->min_height = pattern_size;
+       fse->max_height = OV01A10_NATIVE_HEIGHT - 2 * border_size;
 
        return 0;
 }
@@ -684,31 +713,79 @@ static int ov01a10_get_selection(struct v4l2_subdev *sd,
                                 struct v4l2_subdev_state *state,
                                 struct v4l2_subdev_selection *sel)
 {
-       if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-               return -EINVAL;
+       const int border_size = OV01A10_BAYER_PATTERN_SIZE;
 
        switch (sel->target) {
-       case V4L2_SEL_TGT_NATIVE_SIZE:
+       case V4L2_SEL_TGT_CROP:
+               sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
+               return 0;
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+               sel->r = ov01a10_default_crop;
+               return 0;
        case V4L2_SEL_TGT_CROP_BOUNDS:
-               sel->r.top = 0;
+               /* Keep a border for hvflip shift to preserve bayer-pattern */
+               sel->r.left = border_size;
+               sel->r.top = border_size;
+               sel->r.width = OV01A10_NATIVE_WIDTH - 2 * border_size;
+               sel->r.height = OV01A10_NATIVE_HEIGHT - 2 * border_size;
+               return 0;
+       case V4L2_SEL_TGT_NATIVE_SIZE:
                sel->r.left = 0;
+               sel->r.top = 0;
                sel->r.width = OV01A10_NATIVE_WIDTH;
                sel->r.height = OV01A10_NATIVE_HEIGHT;
                return 0;
-       case V4L2_SEL_TGT_CROP:
-       case V4L2_SEL_TGT_CROP_DEFAULT:
-               sel->r.top = (OV01A10_NATIVE_HEIGHT -
-                             OV01A10_DEFAULT_HEIGHT) / 2;
-               sel->r.left = (OV01A10_NATIVE_WIDTH -
-                              OV01A10_DEFAULT_WIDTH) / 2;
-               sel->r.width = OV01A10_DEFAULT_WIDTH;
-               sel->r.height = OV01A10_DEFAULT_HEIGHT;
-               return 0;
        }
 
        return -EINVAL;
 }
 
+static int ov01a10_set_selection(struct v4l2_subdev *sd,
+                                struct v4l2_subdev_state *sd_state,
+                                struct v4l2_subdev_selection *sel)
+{
+       const int pattern_size = OV01A10_BAYER_PATTERN_SIZE;
+       const int border_size = OV01A10_BAYER_PATTERN_SIZE;
+       struct ov01a10 *ov01a10 = to_ov01a10(sd);
+       struct v4l2_mbus_framefmt *format;
+       struct v4l2_rect *crop;
+       struct v4l2_rect rect;
+
+       if (sel->target != V4L2_SEL_TGT_CROP)
+               return -EINVAL;
+
+       /*
+        * Clamp the boundaries of the crop rectangle to the size of the sensor
+        * pixel array. Align to pattern-size to ensure pattern isn't disrupted.
+        */
+       rect.left = clamp_val(ALIGN(sel->r.left, pattern_size), border_size,
+                             OV01A10_NATIVE_WIDTH - 2 * border_size);
+       rect.top = clamp_val(ALIGN(sel->r.top, pattern_size), border_size,
+                            OV01A10_NATIVE_HEIGHT - 2 * border_size);
+       rect.width = clamp_val(ALIGN(sel->r.width, pattern_size), pattern_size,
+                              OV01A10_NATIVE_WIDTH - rect.left - border_size);
+       rect.height = clamp_val(ALIGN(sel->r.height, pattern_size), 
pattern_size,
+                               OV01A10_NATIVE_HEIGHT - rect.top - border_size);
+
+       crop = v4l2_subdev_state_get_crop(sd_state, sel->pad);
+
+       /* Reset the output size if the crop rectangle size has changed */
+       if (rect.width != crop->width || rect.height != crop->height) {
+               format = v4l2_subdev_state_get_format(sd_state, sel->pad);
+               format->width = rect.width;
+               format->height = rect.height;
+
+               if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+                       ov01a10_update_blank_ctrls(ov01a10, rect.width,
+                                                  rect.height);
+       }
+
+       *crop = rect;
+       sel->r = rect;
+
+       return 0;
+}
+
 static const struct v4l2_subdev_core_ops ov01a10_core_ops = {
        .log_status = v4l2_ctrl_subdev_log_status,
 };
@@ -721,6 +798,7 @@ static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = {
        .set_fmt = ov01a10_set_format,
        .get_fmt = v4l2_subdev_get_fmt,
        .get_selection = ov01a10_get_selection,
+       .set_selection = ov01a10_set_selection,
        .enum_mbus_code = ov01a10_enum_mbus_code,
        .enum_frame_size = ov01a10_enum_frame_size,
 };
@@ -940,8 +1018,6 @@ static int ov01a10_probe(struct i2c_client *client)
        if (ret)
                goto err_power_off;
 
-       ov01a10->cur_mode = &supported_modes[0];
-
        ret = ov01a10_init_controls(ov01a10);
        if (ret)
                goto err_power_off;
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to