* [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver
@ 2026-07-29 8:04 Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 1/4] dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor Wenmeng Liu
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Wenmeng Liu @ 2026-07-29 8:04 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy
Cc: linux-media, devicetree, linux-kernel, Wenmeng Liu, Conor Dooley
Add OmniVision OG0VA1B driver support. The OmniVision OG0VA1B is a
1/10-inch monochrome CMOS VGA image sensor. It outputs 10-bit raw (Y10)
frames at up to 640x480 resolution over a single-lane MIPI CSI-2
interface and is controlled via an I2C-compatible SCCB bus.
This driver has been verified(include tpg) on the Purwa EVK.
Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
Changes in v4:
- Rename the OG0VA1B and OG0VE1B test pattern menu entries to make them
more accurate. -- Vladimir
- Link to v3: https://lore.kernel.org/r/20260708-og0va1b-v3-0-de8e44455a42@oss.qualcomm.com
Changes in v3:
- Generalise the binding title and description for both sensors. -- Vladimir
- Keep the cached pre_isp read so the OG0VE1B test pattern still works,
select it via a per-sensor callback. -- Bryan, Vladimir
- Rename the "sensor" field to "data", drop bpp (derive from code) and
add og0ve1b_pixel_rate(). -- Vladimir
- Sort defines, rename the shared CHIP_ID reg to OG0V_ and order
og0va1b_data first. -- Vladimir
- Expose all four OG0VA1B test patterns instead of one.
- Link to v2: https://lore.kernel.org/r/20260702-og0va1b-v2-0-0071442caa2a@oss.qualcomm.com
Changes in v2:
- Integrate OG0VA1B into the existing og0ve1b driver and binding
instead. -- Vladimir
- Link to v1: https://lore.kernel.org/r/20260618-og0va1b-v1-0-dda71bb83009@oss.qualcomm.com
---
Wenmeng Liu (4):
dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor
media: i2c: og0ve1b: Use monochrome-appropriate test pattern name
media: i2c: og0ve1b: Introduce per-sensor data structure
media: i2c: og0ve1b: Add support for OmniVision OG0VA1B
.../bindings/media/i2c/ovti,og0ve1b.yaml | 15 +-
drivers/media/i2c/og0ve1b.c | 377 ++++++++++++++++++---
2 files changed, 345 insertions(+), 47 deletions(-)
---
base-commit: 3652b49adac266a3d27cb41cdfdb7d8790fc3633
change-id: 20260618-og0va1b-55bbf3cabb0e
Best regards,
--
Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/4] dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor
2026-07-29 8:04 [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver Wenmeng Liu
@ 2026-07-29 8:04 ` Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 2/4] media: i2c: og0ve1b: Use monochrome-appropriate test pattern name Wenmeng Liu
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Wenmeng Liu @ 2026-07-29 8:04 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy
Cc: linux-media, devicetree, linux-kernel, Wenmeng Liu, Conor Dooley
The OmniVision OG0VA1B is a monochrome image sensor closely related to
the already supported OG0VE1B. It shares the same SCCB control
interface, power supplies and MIPI D-PHY description, but differs in
its chip ID, register programming and output format.
Generalise the binding title and description to cover both sensors.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
.../devicetree/bindings/media/i2c/ovti,og0ve1b.yaml | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
index bd2f1ae23e6547032361924a6953000bab1129df..b6fa9645c7c6cb3fa907478684d02b01d916fea1 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
@@ -4,14 +4,13 @@
$id: http://devicetree.org/schemas/media/i2c/ovti,og0ve1b.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: OmniVision OG0VE1B Image Sensor
+title: OmniVision OG0VA1B and OG0VE1B Image Sensors
description:
- OmniVision OG0VE1B image sensor is a low power consuming monochrome
- image sensor. The sensor is controlled over a serial camera control
- bus protocol (SCCB), the widest supported image size is 640x480 at
- 120 frames per second rate, data output format is 8/10-bit RAW
- transferred over one-lane MIPI D-PHY at up to 800 Mbps.
+ OmniVision OG0VA1B and OG0VE1B are low power consuming monochrome image
+ sensors. The sensors are controlled over a serial camera control bus
+ protocol (SCCB), the widest supported image size is 640x480, data output
+ format is 8/10-bit RAW transferred over one-lane MIPI D-PHY.
maintainers:
- Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
@@ -21,7 +20,9 @@ allOf:
properties:
compatible:
- const: ovti,og0ve1b
+ enum:
+ - ovti,og0va1b
+ - ovti,og0ve1b
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 2/4] media: i2c: og0ve1b: Use monochrome-appropriate test pattern name
2026-07-29 8:04 [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 1/4] dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor Wenmeng Liu
@ 2026-07-29 8:04 ` Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 4/4] media: i2c: og0ve1b: Add support for OmniVision OG0VA1B Wenmeng Liu
3 siblings, 0 replies; 6+ messages in thread
From: Wenmeng Liu @ 2026-07-29 8:04 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy
Cc: linux-media, devicetree, linux-kernel, Wenmeng Liu
OG0VE1B is a monochrome sensor and cannot produce coloured test
patterns, so the "Vertical Colour Bars" test pattern name is misleading.
Rename it to "Standard Test Bar" to match the OG0VA1B naming.
Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
drivers/media/i2c/og0ve1b.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
index 84a28cdcade10f8fbcf945999e88f84641b9bc0d..da5c8e49d4a811f55736bd89c6922856f2e7391c 100644
--- a/drivers/media/i2c/og0ve1b.c
+++ b/drivers/media/i2c/og0ve1b.c
@@ -72,7 +72,7 @@ struct og0ve1b_mode {
static const char * const og0ve1b_test_pattern_menu[] = {
"Disabled",
- "Vertical Colour Bars",
+ "Standard Test Bar",
};
static const char * const og0ve1b_supply_names[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure
2026-07-29 8:04 [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 1/4] dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 2/4] media: i2c: og0ve1b: Use monochrome-appropriate test pattern name Wenmeng Liu
@ 2026-07-29 8:04 ` Wenmeng Liu
2026-07-29 8:22 ` sashiko-bot
2026-07-29 8:04 ` [PATCH v4 4/4] media: i2c: og0ve1b: Add support for OmniVision OG0VA1B Wenmeng Liu
3 siblings, 1 reply; 6+ messages in thread
From: Wenmeng Liu @ 2026-07-29 8:04 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy
Cc: linux-media, devicetree, linux-kernel, Wenmeng Liu
Refactor the driver to separate sensor-specific data from the common
driver code. Introduce a sensor_data structure, populated through
i2c_get_match_data(), to hold the chip ID, MCLK frequency, link
frequencies and supported modes.
Test pattern programming is described by a per-sensor callback, and each
mode stores its media bus format code (rather than a bits-per-pixel
value) so that sensors with a different output format can be supported.
Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Tested-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
drivers/media/i2c/og0ve1b.c | 99 ++++++++++++++++++++++++++++++++-------------
1 file changed, 71 insertions(+), 28 deletions(-)
diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
index da5c8e49d4a811f55736bd89c6922856f2e7391c..df428137cfdc1a0936c9bc936d015ed2a649c218 100644
--- a/drivers/media/i2c/og0ve1b.c
+++ b/drivers/media/i2c/og0ve1b.c
@@ -65,11 +65,23 @@ struct og0ve1b_mode {
u32 height; /* Frame height in pixels */
u32 hts; /* Horizontal timing size */
u32 vts; /* Default vertical timing size */
- u32 bpp; /* Bits per pixel */
+ u32 code; /* MEDIA_BUS_FMT code */
const struct og0ve1b_reg_list reg_list; /* Sensor register setting */
};
+struct og0ve1b;
+
+struct og0ve1b_sensor_data {
+ u64 chip_id;
+ unsigned long mclk_freq;
+ int (*enable_test_pattern)(struct og0ve1b *og0ve1b, u32 pattern);
+ const s64 *link_freq_menu;
+ int num_link_freqs;
+ const struct og0ve1b_mode *modes;
+ int num_modes;
+};
+
static const char * const og0ve1b_test_pattern_menu[] = {
"Disabled",
"Standard Test Bar",
@@ -99,6 +111,8 @@ struct og0ve1b {
/* Saved register value */
u64 pre_isp;
+
+ const struct og0ve1b_sensor_data *data;
};
static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = {
@@ -247,13 +261,13 @@ static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = {
{ CCI_REG8(0x3f47), 0x35 },
};
-static const struct og0ve1b_mode supported_modes[] = {
+static const struct og0ve1b_mode og0ve1b_supported_modes[] = {
{
.width = 640,
.height = 480,
.hts = 792,
.vts = 568,
- .bpp = 8,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
.reg_list = {
.regs = og0ve1b_640x480_120fps_mode,
.num_regs = ARRAY_SIZE(og0ve1b_640x480_120fps_mode),
@@ -273,11 +287,21 @@ static int og0ve1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern)
return cci_write(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP, val, NULL);
}
+static const struct og0ve1b_sensor_data og0ve1b_data = {
+ .chip_id = OG0VE1B_CHIP_ID,
+ .mclk_freq = OG0VE1B_MCLK_FREQ_24MHZ,
+ .enable_test_pattern = og0ve1b_enable_test_pattern,
+ .link_freq_menu = og0ve1b_link_freq_menu,
+ .num_link_freqs = ARRAY_SIZE(og0ve1b_link_freq_menu),
+ .modes = og0ve1b_supported_modes,
+ .num_modes = ARRAY_SIZE(og0ve1b_supported_modes),
+};
+
static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct og0ve1b *og0ve1b = container_of(ctrl->handler, struct og0ve1b,
ctrl_handler);
- const struct og0ve1b_mode *mode = &supported_modes[0];
+ const struct og0ve1b_mode *mode = &og0ve1b->data->modes[0];
s64 exposure_max;
int ret;
@@ -314,7 +338,7 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
ctrl->val + mode->height, NULL);
break;
case V4L2_CID_TEST_PATTERN:
- ret = og0ve1b_enable_test_pattern(og0ve1b, ctrl->val);
+ ret = og0ve1b->data->enable_test_pattern(og0ve1b, ctrl->val);
break;
default:
ret = -EINVAL;
@@ -330,10 +354,19 @@ static const struct v4l2_ctrl_ops og0ve1b_ctrl_ops = {
.s_ctrl = og0ve1b_set_ctrl,
};
+static s64 og0ve1b_pixel_rate(const struct og0ve1b_sensor_data *data)
+{
+ const struct og0ve1b_mode *mode = &data->modes[0];
+ unsigned int bpp = mode->code == MEDIA_BUS_FMT_Y8_1X8 ? 8 : 10;
+
+ return data->link_freq_menu[0] / bpp;
+}
+
static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
{
struct v4l2_ctrl_handler *ctrl_hdlr = &og0ve1b->ctrl_handler;
- const struct og0ve1b_mode *mode = &supported_modes[0];
+ const struct og0ve1b_mode *mode = &og0ve1b->data->modes[0];
+ const struct og0ve1b_sensor_data *data = og0ve1b->data;
s64 exposure_max, pixel_rate, h_blank, v_blank;
struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl *ctrl;
@@ -343,12 +376,12 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &og0ve1b_ctrl_ops,
V4L2_CID_LINK_FREQ,
- ARRAY_SIZE(og0ve1b_link_freq_menu) - 1,
- 0, og0ve1b_link_freq_menu);
+ data->num_link_freqs - 1,
+ 0, data->link_freq_menu);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- pixel_rate = og0ve1b_link_freq_menu[0] / mode->bpp;
+ pixel_rate = og0ve1b_pixel_rate(data);
v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_PIXEL_RATE,
0, pixel_rate, 1, pixel_rate);
@@ -407,7 +440,7 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
static void og0ve1b_update_pad_format(const struct og0ve1b_mode *mode,
struct v4l2_mbus_framefmt *fmt)
{
- fmt->code = MEDIA_BUS_FMT_Y8_1X8;
+ fmt->code = mode->code;
fmt->width = mode->width;
fmt->height = mode->height;
fmt->field = V4L2_FIELD_NONE;
@@ -421,8 +454,8 @@ static int og0ve1b_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
{
- const struct og0ve1b_reg_list *reg_list = &supported_modes[0].reg_list;
struct og0ve1b *og0ve1b = to_og0ve1b(sd);
+ const struct og0ve1b_reg_list *reg_list = &og0ve1b->data->modes[0].reg_list;
int ret;
ret = pm_runtime_resume_and_get(og0ve1b->dev);
@@ -484,13 +517,14 @@ static int og0ve1b_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
+ struct og0ve1b *og0ve1b = to_og0ve1b(sd);
struct v4l2_mbus_framefmt *format;
const struct og0ve1b_mode *mode;
format = v4l2_subdev_state_get_format(state, 0);
- mode = v4l2_find_nearest_size(supported_modes,
- ARRAY_SIZE(supported_modes),
+ mode = v4l2_find_nearest_size(og0ve1b->data->modes,
+ og0ve1b->data->num_modes,
width, height,
fmt->format.width,
fmt->format.height);
@@ -505,10 +539,12 @@ static int og0ve1b_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
+ struct og0ve1b *og0ve1b = to_og0ve1b(sd);
+
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_Y8_1X8;
+ code->code = og0ve1b->data->modes[0].code;
return 0;
}
@@ -517,15 +553,18 @@ static int og0ve1b_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))
+ struct og0ve1b *og0ve1b = to_og0ve1b(sd);
+ const struct og0ve1b_sensor_data *data = og0ve1b->data;
+
+ if (fse->index >= data->num_modes)
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_Y8_1X8)
+ if (fse->code != data->modes[fse->index].code)
return -EINVAL;
- fse->min_width = supported_modes[fse->index].width;
+ fse->min_width = data->modes[fse->index].width;
fse->max_width = fse->min_width;
- fse->min_height = supported_modes[fse->index].height;
+ fse->min_height = data->modes[fse->index].height;
fse->max_height = fse->min_height;
return 0;
@@ -534,13 +573,14 @@ static int og0ve1b_enum_frame_size(struct v4l2_subdev *sd,
static int og0ve1b_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
+ const struct og0ve1b_mode *mode = &to_og0ve1b(sd)->data->modes[0];
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_TRY,
.pad = 0,
.format = {
- .code = MEDIA_BUS_FMT_Y8_1X8,
- .width = supported_modes[0].width,
- .height = supported_modes[0].height,
+ .code = mode->code,
+ .width = mode->width,
+ .height = mode->height,
},
};
@@ -586,9 +626,9 @@ static int og0ve1b_identify_sensor(struct og0ve1b *og0ve1b)
return ret;
}
- if (val != OG0VE1B_CHIP_ID) {
- dev_err(og0ve1b->dev, "chip id mismatch: %x!=%llx\n",
- OG0VE1B_CHIP_ID, val);
+ if (val != og0ve1b->data->chip_id) {
+ dev_err(og0ve1b->dev, "chip id mismatch: %llx!=%llx\n",
+ og0ve1b->data->chip_id, val);
return -ENODEV;
}
@@ -624,8 +664,8 @@ static int og0ve1b_check_hwcfg(struct og0ve1b *og0ve1b)
ret = v4l2_link_freq_to_bitmap(og0ve1b->dev,
bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
- og0ve1b_link_freq_menu,
- ARRAY_SIZE(og0ve1b_link_freq_menu),
+ og0ve1b->data->link_freq_menu,
+ og0ve1b->data->num_link_freqs,
&freq_bitmap);
v4l2_fwnode_endpoint_free(&bus_cfg);
@@ -686,6 +726,9 @@ static int og0ve1b_probe(struct i2c_client *client)
return -ENOMEM;
og0ve1b->dev = &client->dev;
+ og0ve1b->data = i2c_get_match_data(client);
+ if (!og0ve1b->data)
+ return -ENODEV;
v4l2_i2c_subdev_init(&og0ve1b->sd, client, &og0ve1b_subdev_ops);
@@ -700,7 +743,7 @@ static int og0ve1b_probe(struct i2c_client *client)
"failed to get XVCLK clock\n");
freq = clk_get_rate(og0ve1b->xvclk);
- if (freq && freq != OG0VE1B_MCLK_FREQ_24MHZ)
+ if (freq && freq != og0ve1b->data->mclk_freq)
return dev_err_probe(og0ve1b->dev, -EINVAL,
"XVCLK clock frequency %lu is not supported\n",
freq);
@@ -819,7 +862,7 @@ static const struct dev_pm_ops og0ve1b_pm_ops = {
};
static const struct of_device_id og0ve1b_of_match[] = {
- { .compatible = "ovti,og0ve1b" },
+ { .compatible = "ovti,og0ve1b", .data = &og0ve1b_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, og0ve1b_of_match);
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 4/4] media: i2c: og0ve1b: Add support for OmniVision OG0VA1B
2026-07-29 8:04 [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver Wenmeng Liu
` (2 preceding siblings ...)
2026-07-29 8:04 ` [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure Wenmeng Liu
@ 2026-07-29 8:04 ` Wenmeng Liu
3 siblings, 0 replies; 6+ messages in thread
From: Wenmeng Liu @ 2026-07-29 8:04 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy
Cc: linux-media, devicetree, linux-kernel, Wenmeng Liu
The OmniVision OG0VA1B is a monochrome image sensor closely related to
the OG0VE1B. It shares the SCCB control interface, power supplies and
the single-lane MIPI D-PHY description, and differs in its chip id, the
test pattern register, the register programming and the output format
(10-bit RAW instead of 8-bit).
Add an og0ve1b_sensor_data entry describing the OG0VA1B together with
its 640x480 60fps register sequence.
Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Tested-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> # og0ve1b
Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
drivers/media/i2c/og0ve1b.c | 278 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 266 insertions(+), 12 deletions(-)
diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
index df428137cfdc1a0936c9bc936d015ed2a649c218..1d220515de4bb199618d0f08ded5f975c7bd04c6 100644
--- a/drivers/media/i2c/og0ve1b.c
+++ b/drivers/media/i2c/og0ve1b.c
@@ -14,10 +14,14 @@
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
+#define OG0VA1B_LINK_FREQ_480MHZ (480 * HZ_PER_MHZ)
+#define OG0VA1B_MCLK_FREQ_19_2MHZ (19200 * HZ_PER_KHZ)
+
#define OG0VE1B_LINK_FREQ_500MHZ (500 * HZ_PER_MHZ)
#define OG0VE1B_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
-#define OG0VE1B_REG_CHIP_ID CCI_REG24(0x300a)
+#define OG0V_REG_CHIP_ID CCI_REG24(0x300a)
+#define OG0VA1B_CHIP_ID 0xc75641
#define OG0VE1B_CHIP_ID 0xc75645
#define OG0VE1B_REG_MODE_SELECT CCI_REG8(0x0100)
@@ -45,12 +49,18 @@
#define OG0VE1B_REG_VTS CCI_REG16(0x380e)
#define OG0VE1B_VTS_MAX 0xffff
-/* Test pattern */
+/* Test pattern - OG0VA1B uses 0x5100, OG0VE1B uses 0x5e00 */
+#define OG0VA1B_REG_TEST_PATTERN CCI_REG8(0x5100)
+#define OG0VA1B_TEST_PATTERN_BAR_SHIFT 2
#define OG0VE1B_REG_PRE_ISP CCI_REG8(0x5e00)
#define OG0VE1B_TEST_PATTERN_ENABLE BIT(7)
#define to_og0ve1b(_sd) container_of(_sd, struct og0ve1b, sd)
+static const s64 og0va1b_link_freq_menu[] = {
+ OG0VA1B_LINK_FREQ_480MHZ,
+};
+
static const s64 og0ve1b_link_freq_menu[] = {
OG0VE1B_LINK_FREQ_500MHZ,
};
@@ -73,15 +83,31 @@ struct og0ve1b_mode {
struct og0ve1b;
struct og0ve1b_sensor_data {
+ const char *name;
u64 chip_id;
unsigned long mclk_freq;
int (*enable_test_pattern)(struct og0ve1b *og0ve1b, u32 pattern);
+ const char * const *test_pattern_menu;
+ int num_test_patterns;
+ bool cache_test_pattern_reg;
+ /* Exposure register unit: OG0VE1B 1/16 line (4), OG0VA1B whole lines (0). */
+ unsigned int exposure_shift;
+ /* Pixel rate multiplier: OG0VA1B uses CSI-2 DDR (2), OG0VE1B keeps 1. */
+ unsigned int pixel_rate_mul;
const s64 *link_freq_menu;
int num_link_freqs;
const struct og0ve1b_mode *modes;
int num_modes;
};
+static const char * const og0va1b_test_pattern_menu[] = {
+ "Disabled",
+ "Standard Test Bar",
+ "Top-Bottom Darker Test Bar",
+ "Right-Left Darker Test Bar",
+ "Bottom-Top Darker Test Bar",
+};
+
static const char * const og0ve1b_test_pattern_menu[] = {
"Disabled",
"Standard Test Bar",
@@ -115,6 +141,196 @@ struct og0ve1b {
const struct og0ve1b_sensor_data *data;
};
+static const struct cci_reg_sequence og0va1b_640x480_60fps_mode[] = {
+ { CCI_REG8(0x0302), 0x31 },
+ { CCI_REG8(0x0303), 0x02 },
+ { CCI_REG8(0x0304), 0x01 },
+ { CCI_REG8(0x0305), 0x90 },
+ { CCI_REG8(0x0306), 0x00 },
+ { CCI_REG8(0x0323), 0x02 },
+ { CCI_REG8(0x0325), 0x68 },
+ { CCI_REG8(0x0326), 0xd8 },
+ { CCI_REG8(0x3006), 0x0e },
+ { CCI_REG8(0x300d), 0x08 },
+ { CCI_REG8(0x3018), 0xf0 },
+ { CCI_REG8(0x301c), 0xf0 },
+ { CCI_REG8(0x3020), 0x20 },
+ { CCI_REG8(0x3040), 0x0f },
+ { CCI_REG8(0x3022), 0x01 },
+ { CCI_REG8(0x3107), 0x40 },
+ { CCI_REG8(0x3216), 0x01 },
+ { CCI_REG8(0x3217), 0x00 },
+ { CCI_REG8(0x3218), 0xc0 },
+ { CCI_REG8(0x3219), 0x55 },
+ { CCI_REG8(0x3506), 0x01 },
+ { CCI_REG8(0x3507), 0x50 },
+ { CCI_REG8(0x3508), 0x01 },
+ { CCI_REG8(0x3509), 0x00 },
+ { CCI_REG8(0x350a), 0x01 },
+ { CCI_REG8(0x350b), 0x00 },
+ { CCI_REG8(0x350c), 0x00 },
+ { CCI_REG8(0x3541), 0x00 },
+ { CCI_REG8(0x3542), 0x40 },
+ { CCI_REG8(0x3605), 0x90 },
+ { CCI_REG8(0x3606), 0x41 },
+ { CCI_REG8(0x3612), 0x00 },
+ { CCI_REG8(0x3620), 0x08 },
+ { CCI_REG8(0x3630), 0x17 },
+ { CCI_REG8(0x3631), 0x99 },
+ { CCI_REG8(0x3639), 0x88 },
+ { CCI_REG8(0x3668), 0x00 },
+ { CCI_REG8(0x3674), 0x00 },
+ { CCI_REG8(0x3677), 0x3f },
+ { CCI_REG8(0x368f), 0x06 },
+ { CCI_REG8(0x36a2), 0x19 },
+ { CCI_REG8(0x36a4), 0xf1 },
+ { CCI_REG8(0x36a5), 0x2d },
+ { CCI_REG8(0x3706), 0x30 },
+ { CCI_REG8(0x370d), 0x72 },
+ { CCI_REG8(0x3713), 0x86 },
+ { CCI_REG8(0x3715), 0x03 },
+ { CCI_REG8(0x3716), 0x00 },
+ { CCI_REG8(0x376d), 0x24 },
+ { CCI_REG8(0x3770), 0x3a },
+ { CCI_REG8(0x3778), 0x00 },
+ { CCI_REG8(0x37a8), 0x03 },
+ { CCI_REG8(0x37a9), 0x00 },
+ { CCI_REG8(0x37df), 0x7d },
+ { CCI_REG8(0x3800), 0x00 },
+ { CCI_REG8(0x3801), 0x00 },
+ { CCI_REG8(0x3802), 0x00 },
+ { CCI_REG8(0x3803), 0x00 },
+ { CCI_REG8(0x3804), 0x02 },
+ { CCI_REG8(0x3805), 0x8f },
+ { CCI_REG8(0x3806), 0x01 },
+ { CCI_REG8(0x3807), 0xef },
+ { CCI_REG8(0x3808), 0x02 },
+ { CCI_REG8(0x3809), 0x80 },
+ { CCI_REG8(0x380a), 0x01 },
+ { CCI_REG8(0x380b), 0xe0 },
+ { CCI_REG8(0x380c), 0x01 },
+ { CCI_REG8(0x380d), 0x78 },
+ { CCI_REG8(0x380e), 0x08 },
+ { CCI_REG8(0x380f), 0x30 },
+ { CCI_REG8(0x3810), 0x00 },
+ { CCI_REG8(0x3811), 0x08 },
+ { CCI_REG8(0x3812), 0x00 },
+ { CCI_REG8(0x3813), 0x08 },
+ { CCI_REG8(0x3814), 0x11 },
+ { CCI_REG8(0x3815), 0x11 },
+ { CCI_REG8(0x3816), 0x00 },
+ { CCI_REG8(0x3817), 0x01 },
+ { CCI_REG8(0x3818), 0x00 },
+ { CCI_REG8(0x3819), 0x05 },
+ { CCI_REG8(0x3820), 0x40 },
+ { CCI_REG8(0x3821), 0x04 },
+ { CCI_REG8(0x3823), 0x00 },
+ { CCI_REG8(0x3826), 0x00 },
+ { CCI_REG8(0x3827), 0x00 },
+ { CCI_REG8(0x382b), 0x52 },
+ { CCI_REG8(0x384a), 0xa2 },
+ { CCI_REG8(0x3858), 0x00 },
+ { CCI_REG8(0x3859), 0x00 },
+ { CCI_REG8(0x3860), 0x00 },
+ { CCI_REG8(0x3861), 0x00 },
+ { CCI_REG8(0x3866), 0x0c },
+ { CCI_REG8(0x3867), 0x07 },
+ { CCI_REG8(0x3884), 0x00 },
+ { CCI_REG8(0x3885), 0x08 },
+ { CCI_REG8(0x3888), 0x50 },
+ { CCI_REG8(0x3893), 0x6c },
+ { CCI_REG8(0x3898), 0x00 },
+ { CCI_REG8(0x389a), 0x04 },
+ { CCI_REG8(0x389b), 0x01 },
+ { CCI_REG8(0x389c), 0x0b },
+ { CCI_REG8(0x389d), 0xdc },
+ { CCI_REG8(0x38b1), 0x04 },
+ { CCI_REG8(0x38b2), 0x00 },
+ { CCI_REG8(0x38b3), 0x08 },
+ { CCI_REG8(0x38c1), 0x46 },
+ { CCI_REG8(0x38c9), 0x02 },
+ { CCI_REG8(0x38d4), 0x06 },
+ { CCI_REG8(0x38d5), 0x5a },
+ { CCI_REG8(0x38d6), 0x08 },
+ { CCI_REG8(0x38d7), 0x3a },
+ { CCI_REG8(0x391f), 0x00 },
+ { CCI_REG8(0x3920), 0xaa },
+ { CCI_REG8(0x3921), 0x00 },
+ { CCI_REG8(0x3922), 0x00 },
+ { CCI_REG8(0x3923), 0x00 },
+ { CCI_REG8(0x3924), 0x00 },
+ { CCI_REG8(0x3925), 0x00 },
+ { CCI_REG8(0x3926), 0x00 },
+ { CCI_REG8(0x3927), 0x00 },
+ { CCI_REG8(0x3928), 0x10 },
+ { CCI_REG8(0x3929), 0x01 },
+ { CCI_REG8(0x392a), 0xb4 },
+ { CCI_REG8(0x392b), 0x00 },
+ { CCI_REG8(0x392c), 0x10 },
+ { CCI_REG8(0x392d), 0x01 },
+ { CCI_REG8(0x392e), 0x78 },
+ { CCI_REG8(0x392f), 0x4a },
+ { CCI_REG8(0x391e), 0x01 },
+ { CCI_REG8(0x389f), 0x08 },
+ { CCI_REG8(0x38a0), 0x00 },
+ { CCI_REG8(0x38a1), 0x00 },
+ { CCI_REG8(0x3a06), 0x06 },
+ { CCI_REG8(0x3a07), 0x78 },
+ { CCI_REG8(0x3a08), 0x08 },
+ { CCI_REG8(0x3a09), 0x80 },
+ { CCI_REG8(0x3a52), 0x00 },
+ { CCI_REG8(0x3a53), 0x01 },
+ { CCI_REG8(0x3a54), 0x0c },
+ { CCI_REG8(0x3a55), 0x04 },
+ { CCI_REG8(0x3a58), 0x0c },
+ { CCI_REG8(0x3a59), 0x04 },
+ { CCI_REG8(0x4000), 0xcf },
+ { CCI_REG8(0x4003), 0x40 },
+ { CCI_REG8(0x4008), 0x04 },
+ { CCI_REG8(0x4009), 0x13 },
+ { CCI_REG8(0x400a), 0x02 },
+ { CCI_REG8(0x400b), 0x34 },
+ { CCI_REG8(0x4010), 0x71 },
+ { CCI_REG8(0x4042), 0xc3 },
+ { CCI_REG8(0x4306), 0x04 },
+ { CCI_REG8(0x4307), 0x12 },
+ { CCI_REG8(0x4500), 0x70 },
+ { CCI_REG8(0x4509), 0x00 },
+ { CCI_REG8(0x450b), 0x83 },
+ { CCI_REG8(0x4604), 0x68 },
+ { CCI_REG8(0x481b), 0x44 },
+ { CCI_REG8(0x481f), 0x30 },
+ { CCI_REG8(0x4823), 0x44 },
+ { CCI_REG8(0x4825), 0x35 },
+ { CCI_REG8(0x4837), 0x11 },
+ { CCI_REG8(0x4f00), 0x04 },
+ { CCI_REG8(0x4f10), 0x04 },
+ { CCI_REG8(0x4f21), 0x01 },
+ { CCI_REG8(0x4f22), 0x00 },
+ { CCI_REG8(0x4f23), 0x54 },
+ { CCI_REG8(0x4f24), 0x51 },
+ { CCI_REG8(0x4f25), 0x41 },
+ { CCI_REG8(0x5000), 0x3f },
+ { CCI_REG8(0x5001), 0x80 },
+ { CCI_REG8(0x500a), 0x00 },
+ { CCI_REG8(0x5100), 0x00 },
+ { CCI_REG8(0x5111), 0x20 },
+};
+
+static const struct og0ve1b_mode og0va1b_supported_modes[] = {
+ {
+ .width = 640,
+ .height = 480,
+ .hts = 752,
+ .vts = 2096,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .reg_list = {
+ .regs = og0va1b_640x480_60fps_mode,
+ .num_regs = ARRAY_SIZE(og0va1b_640x480_60fps_mode),
+ },
+ },
+};
+
static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = {
{ CCI_REG8(0x30a0), 0x02 },
{ CCI_REG8(0x30a1), 0x00 },
@@ -275,6 +491,17 @@ static const struct og0ve1b_mode og0ve1b_supported_modes[] = {
},
};
+static int og0va1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern)
+{
+ u64 val = 0;
+
+ if (pattern)
+ val = ((pattern - 1) << OG0VA1B_TEST_PATTERN_BAR_SHIFT) |
+ OG0VE1B_TEST_PATTERN_ENABLE;
+
+ return cci_write(og0ve1b->regmap, OG0VA1B_REG_TEST_PATTERN, val, NULL);
+}
+
static int og0ve1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern)
{
u64 val = og0ve1b->pre_isp;
@@ -287,10 +514,31 @@ static int og0ve1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern)
return cci_write(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP, val, NULL);
}
+static const struct og0ve1b_sensor_data og0va1b_data = {
+ .name = "og0va1b",
+ .chip_id = OG0VA1B_CHIP_ID,
+ .mclk_freq = OG0VA1B_MCLK_FREQ_19_2MHZ,
+ .enable_test_pattern = og0va1b_enable_test_pattern,
+ .test_pattern_menu = og0va1b_test_pattern_menu,
+ .num_test_patterns = ARRAY_SIZE(og0va1b_test_pattern_menu),
+ .exposure_shift = 0,
+ .pixel_rate_mul = 2,
+ .link_freq_menu = og0va1b_link_freq_menu,
+ .num_link_freqs = ARRAY_SIZE(og0va1b_link_freq_menu),
+ .modes = og0va1b_supported_modes,
+ .num_modes = ARRAY_SIZE(og0va1b_supported_modes),
+};
+
static const struct og0ve1b_sensor_data og0ve1b_data = {
+ .name = "og0ve1b",
.chip_id = OG0VE1B_CHIP_ID,
.mclk_freq = OG0VE1B_MCLK_FREQ_24MHZ,
.enable_test_pattern = og0ve1b_enable_test_pattern,
+ .test_pattern_menu = og0ve1b_test_pattern_menu,
+ .num_test_patterns = ARRAY_SIZE(og0ve1b_test_pattern_menu),
+ .cache_test_pattern_reg = true,
+ .exposure_shift = 4,
+ .pixel_rate_mul = 1,
.link_freq_menu = og0ve1b_link_freq_menu,
.num_link_freqs = ARRAY_SIZE(og0ve1b_link_freq_menu),
.modes = og0ve1b_supported_modes,
@@ -331,7 +579,8 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_EXPOSURE:
ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_EXPOSURE,
- ctrl->val << 4, NULL);
+ ctrl->val << og0ve1b->data->exposure_shift,
+ NULL);
break;
case V4L2_CID_VBLANK:
ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_VTS,
@@ -359,7 +608,7 @@ static s64 og0ve1b_pixel_rate(const struct og0ve1b_sensor_data *data)
const struct og0ve1b_mode *mode = &data->modes[0];
unsigned int bpp = mode->code == MEDIA_BUS_FMT_Y8_1X8 ? 8 : 10;
- return data->link_freq_menu[0] / bpp;
+ return data->link_freq_menu[0] * data->pixel_rate_mul / bpp;
}
static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
@@ -412,8 +661,8 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og0ve1b_ctrl_ops,
V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(og0ve1b_test_pattern_menu) - 1,
- 0, 0, og0ve1b_test_pattern_menu);
+ data->num_test_patterns - 1,
+ 0, 0, data->test_pattern_menu);
if (ctrl_hdlr->error)
return ctrl_hdlr->error;
@@ -620,7 +869,7 @@ static int og0ve1b_identify_sensor(struct og0ve1b *og0ve1b)
u64 val;
int ret;
- ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_CHIP_ID, &val, NULL);
+ ret = cci_read(og0ve1b->regmap, OG0V_REG_CHIP_ID, &val, NULL);
if (ret) {
dev_err(og0ve1b->dev, "failed to read chip id: %d\n", ret);
return ret;
@@ -632,10 +881,12 @@ static int og0ve1b_identify_sensor(struct og0ve1b *og0ve1b)
return -ENODEV;
}
- ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP,
- &og0ve1b->pre_isp, NULL);
- if (ret)
- dev_err(og0ve1b->dev, "failed to read pre_isp: %d\n", ret);
+ if (og0ve1b->data->cache_test_pattern_reg) {
+ ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP,
+ &og0ve1b->pre_isp, NULL);
+ if (ret)
+ dev_err(og0ve1b->dev, "failed to read pre_isp: %d\n", ret);
+ }
return ret;
}
@@ -731,6 +982,8 @@ static int og0ve1b_probe(struct i2c_client *client)
return -ENODEV;
v4l2_i2c_subdev_init(&og0ve1b->sd, client, &og0ve1b_subdev_ops);
+ v4l2_i2c_subdev_set_name(&og0ve1b->sd, client,
+ og0ve1b->data->name, NULL);
og0ve1b->regmap = devm_cci_regmap_init_i2c(client, 16);
if (IS_ERR(og0ve1b->regmap))
@@ -862,6 +1115,7 @@ static const struct dev_pm_ops og0ve1b_pm_ops = {
};
static const struct of_device_id og0ve1b_of_match[] = {
+ { .compatible = "ovti,og0va1b", .data = &og0va1b_data },
{ .compatible = "ovti,og0ve1b", .data = &og0ve1b_data },
{ /* sentinel */ }
};
@@ -880,5 +1134,5 @@ static struct i2c_driver og0ve1b_i2c_driver = {
module_i2c_driver(og0ve1b_i2c_driver);
MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>");
-MODULE_DESCRIPTION("OmniVision OG0VE1B sensor driver");
+MODULE_DESCRIPTION("OmniVision OG0VE1B/OG0VA1B sensor driver");
MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure
2026-07-29 8:04 ` [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure Wenmeng Liu
@ 2026-07-29 8:22 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-07-29 8:22 UTC (permalink / raw)
To: Wenmeng Liu; +Cc: robh, conor+dt, media-ci, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Build failure on 32-bit architectures due to 64-bit division without kernel math helpers.
--
--- Patch [3]: [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure ---
commit 44a11606f1ac2c69b89a34ee20cb6650216651a9
Author: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
Subject: media: i2c: og0ve1b: Introduce per-sensor data structure
This commit refactors the driver to separate sensor-specific data from the
common driver code. It introduces a sensor_data structure, populated through
i2c_get_match_data(), to hold chip ID, MCLK frequency, link frequencies, and
supported modes.
> diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
> index da5c8e49d4a81..df428137cfdc1 100644
> --- a/drivers/media/i2c/og0ve1b.c
> +++ b/drivers/media/i2c/og0ve1b.c
[ ... ]
> @@ -330,10 +354,19 @@ static const struct v4l2_ctrl_ops og0ve1b_ctrl_ops = {
> .s_ctrl = og0ve1b_set_ctrl,
> };
>
> +static s64 og0ve1b_pixel_rate(const struct og0ve1b_sensor_data *data)
> +{
> + const struct og0ve1b_mode *mode = &data->modes[0];
> + unsigned int bpp = mode->code == MEDIA_BUS_FMT_Y8_1X8 ? 8 : 10;
> +
> + return data->link_freq_menu[0] / bpp;
[Severity: Low]
Will this 64-bit division cause a linker error on 32-bit architectures?
Because data->link_freq_menu[0] is an s64 and the bpp divisor is evaluated at
runtime, the compiler can no longer constant-fold the division. On 32-bit
architectures (like i386 or arm32), a native 64-bit division by a 32-bit
variable results in an undefined reference to __divdi3 or similar.
Does this code need to use div_s64() or div_u64() instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729-og0va1b-v4-0-ec7e7ed87b2b@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-29 8:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 8:04 [PATCH v4 0/4] media: i2c: Add OmniVision OG0VA1B camera sensor driver Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 1/4] dt-bindings: media: i2c: og0ve1b: Add OmniVision OG0VA1B camera sensor Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 2/4] media: i2c: og0ve1b: Use monochrome-appropriate test pattern name Wenmeng Liu
2026-07-29 8:04 ` [PATCH v4 3/4] media: i2c: og0ve1b: Introduce per-sensor data structure Wenmeng Liu
2026-07-29 8:22 ` sashiko-bot
2026-07-29 8:04 ` [PATCH v4 4/4] media: i2c: og0ve1b: Add support for OmniVision OG0VA1B Wenmeng Liu
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.