From: Hans de Goede <hansg@kernel.org>
To: Bingbu Cao <bingbu.cao@intel.com>,
Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: Hans de Goede <hansg@kernel.org>, linux-media@vger.kernel.org
Subject: [PATCH 16/25] media: i2c: ov01a10: Add cropping support / allow arbitrary sizes
Date: Tue, 14 Oct 2025 19:40:24 +0200 [thread overview]
Message-ID: <20251014174033.20534-17-hansg@kernel.org> (raw)
In-Reply-To: <20251014174033.20534-1-hansg@kernel.org>
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 <hansg@kernel.org>
---
drivers/media/i2c/ov01a10.c | 344 ++++++++++++++++++++++--------------
1 file changed, 210 insertions(+), 134 deletions(-)
diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c
index f3bcb61c88dd..e8ccb295fdc9 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},
@@ -261,7 +237,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,
@@ -269,19 +245,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[] = {
@@ -304,7 +272,6 @@ struct ov01a10 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
- const struct ov01a10_mode *cur_mode;
u32 link_freq_index;
struct clk *clk;
@@ -318,6 +285,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;
@@ -340,13 +323,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);
@@ -358,10 +344,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);
@@ -375,12 +365,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,
@@ -407,7 +398,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:
@@ -441,7 +432,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;
@@ -454,8 +444,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,
@@ -466,14 +454,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);
@@ -485,7 +473,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,
@@ -525,24 +513,48 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
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) {
@@ -550,9 +562,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;
@@ -601,54 +618,64 @@ static int ov01a10_set_stream(struct v4l2_subdev *sd, int enable)
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;
+ unsigned int width, height;
- mode = v4l2_find_nearest_size(supported_modes,
- ARRAY_SIZE(supported_modes), width,
- height, fmt->format.width,
- fmt->format.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);
- 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);
+ /* 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;
}
@@ -669,14 +696,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;
}
@@ -685,31 +714,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,
};
@@ -722,6 +799,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,
};
@@ -941,8 +1019,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.51.0
next prev parent reply other threads:[~2025-10-14 17:41 UTC|newest]
Thread overview: 65+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-14 17:40 [PATCH 00/25] media: i2c: ov01a10: Add crop, ov01a1b and ov01a1s support Hans de Goede
2025-10-14 17:40 ` [PATCH 01/25] media: i2c: ov01a10: Fix the horizontal flip control Hans de Goede
2025-10-27 19:00 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 02/25] media: i2c: ov01a10: Fix reported pixel-rate value Hans de Goede
2025-10-27 19:03 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 03/25] media: i2c: ov01a10: Fix gain range Hans de Goede
2025-10-27 19:14 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 04/25] media: i2c: ov01a10: Add missing v4l2_subdev_cleanup() calls Hans de Goede
2025-10-15 2:37 ` Bingbu Cao
2025-10-15 2:46 ` Bingbu Cao
2025-10-28 11:24 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 05/25] media: i2c: ov01a10: Fix passing stream instead of pad to v4l2_subdev_state_get_format() Hans de Goede
2025-10-28 11:40 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 06/25] media: i2c: ov01a10: Fix test-pattern disabling Hans de Goede
2025-10-15 2:34 ` Bingbu Cao
2025-10-28 12:08 ` Mehdi Djait
2025-10-28 14:38 ` Hans de Goede
2025-10-28 15:38 ` Mehdi Djait
2025-10-28 15:52 ` Hans de Goede
2025-10-29 17:44 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 07/25] media: i2c: ov01a10: Change default vblank value to a vblank resulting in 30 fps Hans de Goede
2025-10-28 16:57 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 08/25] media: i2c: ov01a10: Convert to new CCI register access helpers Hans de Goede
2025-10-28 17:01 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 09/25] media: i2c: ov01a10: Remove overly verbose probe() error reporting Hans de Goede
2025-10-28 17:02 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 10/25] media: i2c: ov01a10: Store dev pointer in struct ov01a10 Hans de Goede
2025-10-28 17:18 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 11/25] media: i2c: ov01a10: Add ov01a10_check_hwcfg() function Hans de Goede
2025-10-28 17:29 ` Mehdi Djait
2025-10-28 20:09 ` Hans de Goede
2025-10-29 17:30 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 12/25] media: i2c: ov01a10: Add power on/off sequencing support Hans de Goede
2025-10-28 18:06 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 13/25] media: i2c: ov01a10: Don't update pixel_rate and link_freq from set_fmt Hans de Goede
2025-10-28 18:15 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 14/25] media: i2c: ov01a10: Move setting of ctrl->flags to after checking ctrl_hdlr->error Hans de Goede
2025-10-28 18:18 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 15/25] media: i2c: ov01a10: Use native and default for pixel-array size names Hans de Goede
2025-10-28 19:01 ` Mehdi Djait
2025-10-28 20:19 ` Hans de Goede
2025-10-14 17:40 ` Hans de Goede [this message]
2025-11-06 15:28 ` [PATCH 16/25] media: i2c: ov01a10: Add cropping support / allow arbitrary sizes Mehdi Djait
2025-10-14 17:40 ` [PATCH 17/25] media: i2c: ov01a10: Remove struct ov01a10_reg_list Hans de Goede
2025-10-28 19:13 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 18/25] media: i2c: ov01a10: Replace exposure->min/step with direct define use Hans de Goede
2025-10-28 19:19 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 19/25] media: i2c: ov01a10: Only set register 0x0305 once Hans de Goede
2025-10-28 19:25 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 20/25] media: i2c: ov01a10: Remove values set by controls from global_setting[] Hans de Goede
2025-10-29 17:50 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 21/25] media: i2c: ov01a10: Add ov01a10_sensor_cfg struct Hans de Goede
2025-11-06 15:33 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 22/25] media: i2c: ov01a10: Optimize setting h/vflip values Hans de Goede
2025-11-06 15:54 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 23/25] media: i2c: ov01a10: Add ov01a1b support Hans de Goede
2025-11-06 16:16 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 24/25] media: i2c: ov01a10: Add ov01a1s support Hans de Goede
2025-11-06 16:17 ` Mehdi Djait
2025-10-14 17:40 ` [PATCH 25/25] media: i2c: ov01a10: Register tweaks for ov01a1s model Hans de Goede
2025-10-15 10:45 ` kernel test robot
2025-11-07 9:17 ` Mehdi Djait
2025-11-13 9:54 ` Hans de Goede
2025-11-17 8:17 ` Mehdi Djait
2025-10-28 20:06 ` [PATCH 00/25] media: i2c: ov01a10: Add crop, ov01a1b and ov01a1s support Hans de Goede
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251014174033.20534-17-hansg@kernel.org \
--to=hansg@kernel.org \
--cc=bingbu.cao@intel.com \
--cc=linux-media@vger.kernel.org \
--cc=sakari.ailus@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).