From 896f4e21af4f53719f841b647d084f4689d5ad6c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 14 Oct 2025 19:40:24 +0200 Subject: [PATCH] media: i2c: ov01a10: Add cropping support / allow arbitrary sizes 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 Tested-by: Mehdi Djait # Dell XPS 9315 Reviewed-by: Mehdi Djait Signed-off-by: Sakari Ailus Signed-off-by: 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 3eb6445b8f00b..349fd3d06df5c 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; -- 2.47.3