[PATCH 07/13] media: i2c: os05b10: add 12-bit RAW mode support

From: Tarang Raval

Date: Fri Mar 06 2026 - 07:35:45 EST


Expose a 12-bit Bayer output option in the OS05B10 V4L2 sub-device driver.

Add a 12-bit mode table alongside the existing 10-bit mode, extend the
enumerated mbus codes to include RAW12, and select the correct mode table
based on the requested mbus format in enum_frame_size and stream enable.

Also move OS05B10_REG_MIPI_SC_CTRL_1 programming out of the common register
list and program it at stream-on depending on the selected mode bpp (10/12).

Signed-off-by: Tarang Raval <tarang.raval@xxxxxxxxxxxxxxxxx>
---
drivers/media/i2c/os05b10.c | 97 +++++++++++++++++++++++++++++++------
1 file changed, 81 insertions(+), 16 deletions(-)

diff --git a/drivers/media/i2c/os05b10.c b/drivers/media/i2c/os05b10.c
index fbc191e1d505..d51b7d18d28a 100644
--- a/drivers/media/i2c/os05b10.c
+++ b/drivers/media/i2c/os05b10.c
@@ -143,7 +143,6 @@ static const struct cci_reg_sequence os05b10_common_regs[] = {
{ OS05B10_REG_PLL_CTRL_06, 0x00 },
{ OS05B10_REG_PLL_CTRL_25, 0x3b },
{ OS05B10_REG_MIPI_SC_CTRL, 0x72 },
- { OS05B10_REG_MIPI_SC_CTRL_1, 0x01 },
{ OS05B10_REG_ANALOG_GAIN_SHORT, 0x0080 },
{ OS05B10_REG_DIGITAL_GAIN_SHORT, 0x0400 },
{ OS05B10_REG_EXPOSURE_SHORT, 0x000020 },
@@ -501,6 +500,21 @@ struct os05b10_mode {
struct os05b10_reg_list reg_list;
};

+static const struct os05b10_mode supported_modes_12bit[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .vts = 2007,
+ .hts = 1744,
+ .exp = 1900,
+ .bpp = 12,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592_1944_regs),
+ .regs = mode_2592_1944_regs,
+ },
+ },
+};
+
static const struct os05b10_mode supported_modes_10bit[] = {
{
.width = 2592,
@@ -522,6 +536,7 @@ static const s64 link_frequencies[] = {

static const u32 os05b10_mbus_codes[] = {
MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
};

static const char * const os05b10_test_pattern_menu[] = {
@@ -553,12 +568,19 @@ static inline struct os05b10 *to_os05b10(struct v4l2_subdev *sd)
return container_of_const(sd, struct os05b10, sd);
};

-static u32 os05b10_get_format_code(struct os05b10 *os05b10)
+static u32 os05b10_get_format_code(struct os05b10 *os05b10, u8 bpp)
{
- static const u32 codes[2][2] = {
- { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
- { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
- };
+ static const u32 codes_12[2][2] = {
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, },
+ };
+
+ static const u32 codes_10[2][2] = {
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
+ };
+
+ const u32 (*codes)[2] = (bpp == 12) ? codes_12 : codes_10;

u32 code = codes[os05b10->vflip->val][os05b10->hflip->val];

@@ -653,8 +675,8 @@ static int os05b10_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index >= ARRAY_SIZE(os05b10_mbus_codes))
return -EINVAL;

- code->code = os05b10_get_format_code(os05b10);
-
+ code->code = os05b10_get_format_code(os05b10,
+ (code->index == 1) ? 12 : 10);
return 0;
}

@@ -683,15 +705,42 @@ static int os05b10_set_framing_limits(struct os05b10 *os05b10,
OS05B10_EXPOSURE_STEP, mode->exp);
}

+static inline void get_mode_table(unsigned int code,
+ const struct os05b10_mode **mode_list,
+ unsigned int *num_modes)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ *mode_list = supported_modes_12bit;
+ *num_modes = ARRAY_SIZE(supported_modes_12bit);
+ break;
+
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ *mode_list = supported_modes_10bit;
+ *num_modes = ARRAY_SIZE(supported_modes_10bit);
+ break;
+ default:
+ *mode_list = NULL;
+ *num_modes = 0;
+ break;
+ }
+}
+
static int os05b10_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- const struct os05b10_mode *mode = &supported_modes_10bit[0];
struct os05b10 *os05b10 = to_os05b10(sd);
+ const struct os05b10_mode *mode_list;
struct v4l2_mbus_framefmt *format;
+ const struct os05b10_mode *mode;
+ unsigned int num_modes;
int ret;

+ get_mode_table(fmt->format.code, &mode_list, &num_modes);
+ mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
+ fmt->format.width, fmt->format.height);
+
fmt->format.width = mode->width;
fmt->format.height = mode->height;
fmt->format.field = V4L2_FIELD_NONE;
@@ -730,16 +779,22 @@ static int os05b10_get_selection(struct v4l2_subdev *sd,
}
}

+
static int os05b10_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_10bit))
+ const struct os05b10_mode *mode_list;
+ unsigned int num_modes;
+
+ get_mode_table(fse->code, &mode_list, &num_modes);
+
+ if (fse->index >= num_modes)
return -EINVAL;

- fse->min_width = supported_modes_10bit[fse->index].width;
+ fse->min_width = mode_list[fse->index].width;
fse->max_width = fse->min_width;
- fse->min_height = supported_modes_10bit[fse->index].height;
+ fse->min_height = mode_list[fse->index].height;
fse->max_height = fse->min_height;

return 0;
@@ -752,13 +807,15 @@ static int os05b10_enable_streams(struct v4l2_subdev *sd,
struct os05b10 *os05b10 = to_os05b10(sd);
const struct os05b10_reg_list *reg_list;
const struct v4l2_mbus_framefmt *fmt;
+ const struct os05b10_mode *mode_list;
const struct os05b10_mode *mode;
+ unsigned int num_modes;
int ret;

fmt = v4l2_subdev_state_get_format(state, 0);
- mode = v4l2_find_nearest_size(supported_modes_10bit,
- ARRAY_SIZE(supported_modes_10bit), width,
- height, fmt->width, fmt->height);
+ get_mode_table(fmt->code, &mode_list, &num_modes);
+ mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
+ fmt->width, fmt->height);

ret = pm_runtime_resume_and_get(os05b10->dev);
if (ret < 0)
@@ -772,6 +829,14 @@ static int os05b10_enable_streams(struct v4l2_subdev *sd,
goto err_rpm_put;
}

+ ret = cci_write(os05b10->cci, OS05B10_REG_MIPI_SC_CTRL_1,
+ (mode->bpp == 12) ? OS05B10_12BIT_MODE :
+ OS05B10_10BIT_MODE, NULL);
+ if (ret) {
+ dev_err(os05b10->dev, "failed to write pixel bit registers\n");
+ goto err_rpm_put;
+ }
+
/* Write sensor mode registers */
reg_list = &mode->reg_list;
ret = cci_multi_reg_write(os05b10->cci, reg_list->regs,
@@ -834,7 +899,7 @@ static int os05b10_init_state(struct v4l2_subdev *sd,
format = v4l2_subdev_state_get_format(state, 0);

mode = &supported_modes_10bit[0];
- format->code = os05b10_get_format_code(os05b10);
+ format->code = os05b10_get_format_code(os05b10, 10);

/* Update image pad formate */
format->width = mode->width;
--
2.34.1