This is an 8 megapixel raw10/raw12 sensor with HDR capabilities. HDR mode control is supported, with one HDR mode: staggered HDR with 2 exposures on separate virtual channels. However, for now, only one exposure (VC 0) is accessible via get_frame_desc. Supported resolutions: - 1920 x 1080 @ 60fps (SBGGR10, no HDR) - 1920 x 1080 @ 30fps (SBGGR10, HDR) - 3840 x 2160 @ 30fps (SBGGR12, no HDR) - 3840 x 2160 @ 15fps (SBGGR12, HDR) - 3840 x 2160 @ 30fps (SBGGR10, no HDR) - 3840 x 2160 @ 15fps (SBGGR10, HDR) Signed-off-by: Mirela Rabulea <mirela.rabulea@xxxxxxx> --- Changes in v4: Switch to Y media bus codes. The CFA pattern control will be implemented when patches get merged, or maybe separatelly as RFC? Add pixel_rate member to mode struct, remove fps member. We do not have information how to calculate the pixel rate from the PLL parameters that can be made public. Use register macros for the registers that are documented. User register group macros, where individual registers are not documented Constify more structs Remove some unneded ending commas after a terminator Fix a seeries of smatch warnings like: warning: symbol 'os08a20_init_setting_common' was not declared. Should it be static? Shorten some more lines to 80 columns Changes in v3: Don't hardcode timing registers: remove timing registers x_output_size/y_output_size from register configuration list, add them to ox05b1s_apply_current_mode Remove HTS,VTS from register config list as they are written by HBLANK and VBLANK controls through __v4l2_ctrl_handler_setup Use const for os08a20_supported_modes os08a20 register config cleaning (remove all registers that were at their default value, and more, keep only what seems mandatory to be able to stream) Let the 4k 10bit mode by default without hdr, all 3 modes are now by default without hdr, staggered hdr may be enabled via v4l2-ctl for any of them. Separate the 10/12 bit register settings into separate lists: os08a20_init_setting_10bit, os08a20_init_setting_12bit Update commit description: rearrange supported resolutions and remove 1 duplicate line, state HDR limitation Increase a bit the default vts for 1080p, to get exactly 60fps, it was 62.61 Use regmap_update_bits() directly and remove ox05b1s_regmap_update_bits() Changes in v2: Add spaces inside brackets, wrap lines to 80 Remove some redundant initialization Use a loop in os08a20_enable_staggered_hdr/os08a20_disable_staggered_hdr, for that, add a register settings array for HDR enabling/disabling Make "sizes" a pointer Remove mode headers, add supported modes in the dedicated c file, ox05b1s_modes.c Refactor register lists, for os08a20 use a common list for all modes, and also specific lists per mode drivers/media/i2c/ox05b1s/ox05b1s.h | 4 + drivers/media/i2c/ox05b1s/ox05b1s_mipi.c | 187 +++++++++++++++++++++- drivers/media/i2c/ox05b1s/ox05b1s_modes.c | 110 +++++++++++++ 3 files changed, 300 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ox05b1s/ox05b1s.h b/drivers/media/i2c/ox05b1s/ox05b1s.h index 2a87d69864f9..58454e10150b 100644 --- a/drivers/media/i2c/ox05b1s/ox05b1s.h +++ b/drivers/media/i2c/ox05b1s/ox05b1s.h @@ -17,6 +17,10 @@ struct ox05b1s_reglist { const struct ox05b1s_reg *regs; }; +extern const struct ox05b1s_reglist os08a20_reglist_4k_10b[]; +extern const struct ox05b1s_reglist os08a20_reglist_4k_12b[]; +extern const struct ox05b1s_reglist os08a20_reglist_1080p_10b[]; + extern const struct ox05b1s_reglist ox05b1s_reglist_2592x1944[]; #endif /* OX05B1S_H */ diff --git a/drivers/media/i2c/ox05b1s/ox05b1s_mipi.c b/drivers/media/i2c/ox05b1s/ox05b1s_mipi.c index 1026216ddd5b..bc578fc61f1f 100644 --- a/drivers/media/i2c/ox05b1s/ox05b1s_mipi.c +++ b/drivers/media/i2c/ox05b1s/ox05b1s_mipi.c @@ -40,6 +40,7 @@ struct ox05b1s_sizes { const struct v4l2_area *sizes; }; +struct ox05b1s; struct ox05b1s_plat_data { char name[20]; u32 chip_id; @@ -52,6 +53,9 @@ struct ox05b1s_plat_data { const struct ox05b1s_mode *supported_modes; u32 default_mode_index; const struct ox05b1s_sizes *supported_codes; + const char * const *hdr_modes; + u32 hdr_modes_count; + int (*set_hdr_mode)(struct ox05b1s *sensor, u32 hdr_mode); }; struct ox05b1s_ctrls { @@ -62,6 +66,7 @@ struct ox05b1s_ctrls { struct v4l2_ctrl *vblank; struct v4l2_ctrl *gain; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hdr_mode; }; struct ox05b1s_mode { @@ -102,6 +107,87 @@ struct ox05b1s { struct ox05b1s_ctrls ctrls; }; +#define OS08A20_PIXEL_RATE_144M 144000000 +#define OS08A20_PIXEL_RATE_288M 288000000 +static const struct ox05b1s_mode os08a20_supported_modes[] = { + { + /* 1080p BGGR10, no hdr, 60fps */ + .index = 0, + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_Y10_1X10, + .bpp = 10, + .vts = 0x4d3, + .hts = 0x790, + .exp = 0x4d3 - 8, + .h_bin = true, + .pixel_rate = OS08A20_PIXEL_RATE_144M, + .reg_data = os08a20_reglist_1080p_10b, + }, { + /* 4k BGGR10, no hdr, 30fps */ + .index = 1, + .width = 3840, + .height = 2160, + .code = MEDIA_BUS_FMT_Y10_1X10, + .bpp = 10, + .vts = 0x90a, + .hts = 0x818, + .exp = 0x90a - 8, + .h_bin = false, + .pixel_rate = OS08A20_PIXEL_RATE_288M, + .reg_data = os08a20_reglist_4k_10b, + }, { + /* 4k BGGR12, no hdr, 30fps */ + .index = 2, + .width = 3840, + .height = 2160, + .code = MEDIA_BUS_FMT_Y12_1X12, + .bpp = 12, + .vts = 0x8f0, + .hts = 0x814, + .exp = 0x8f0 - 8, + .h_bin = false, + .pixel_rate = OS08A20_PIXEL_RATE_288M, + .reg_data = os08a20_reglist_4k_12b, + }, { + /* sentinel */ + } +}; + +/* keep in sync with os08a20_supported_modes */ +static const struct v4l2_area os08a20_sbggr10_sizes[] = { + { + .width = 1920, + .height = 1080, + }, { + .width = 3840, + .height = 2160, + }, { + /* sentinel */ + } +}; + +static const struct v4l2_area os08a20_sbggr12_sizes[] = { + { + .width = 3840, + .height = 2160, + }, { + /* sentinel */ + } +}; + +static const struct ox05b1s_sizes os08a20_supported_codes[] = { + { + .code = MEDIA_BUS_FMT_Y10_1X10, + .sizes = os08a20_sbggr10_sizes, + }, { + .code = MEDIA_BUS_FMT_Y12_1X12, + .sizes = os08a20_sbggr12_sizes, + }, { + /* sentinel */ + } +}; + #define OX05B1S_PIXEL_RATE_48M 48000000 static const struct ox05b1s_mode ox05b1s_supported_modes[] = { { @@ -231,6 +317,62 @@ static int ox05b1s_write_reg_array(struct ox05b1s *sensor, return 0; } +static const char * const os08a20_hdr_modes[] = { + "NO HDR", /* No HDR, single exposure */ + "HDR Staggered", /* Staggered HDR mode, 2 exposures on separate VC */ +}; + +static const struct ox05b1s_reg os08a20_init_setting_hdr_en[] = { + { 0x3661, BIT(0) }, /* CORE1[0] STG_HDR_ALIGN_EN */ + { 0x3821, BIT(5) }, /* FORMAT2[5] STG_HDR_EN */ + { 0x4813, BIT(3) }, /* MIPI_CTRL_13[3] */ + { 0x486e, BIT(2) }, /* MIPI_CTRL_6E[2] MIPI_VC_ENABLE */ +}; + +static int os08a20_enable_staggered_hdr(struct ox05b1s *sensor) +{ + int ret; + + for (int i = 0; i < ARRAY_SIZE(os08a20_init_setting_hdr_en); i++) { + ret = regmap_update_bits(sensor->regmap, + os08a20_init_setting_hdr_en[i].addr, + os08a20_init_setting_hdr_en[i].data, + os08a20_init_setting_hdr_en[i].data); + if (ret) + return ret; + } + + return 0; +} + +static int os08a20_disable_staggered_hdr(struct ox05b1s *sensor) +{ + int ret; + + for (int i = 0; i < ARRAY_SIZE(os08a20_init_setting_hdr_en); i++) { + ret = regmap_update_bits(sensor->regmap, + os08a20_init_setting_hdr_en[i].addr, + os08a20_init_setting_hdr_en[i].data, + 0); + if (ret) + return ret; + } + + return 0; +} + +static int os08a20_set_hdr_mode(struct ox05b1s *sensor, u32 hdr_mode) +{ + switch (hdr_mode) { + case 0: + return os08a20_disable_staggered_hdr(sensor); + case 1: + return os08a20_enable_staggered_hdr(sensor); + default: + return -EINVAL; + } +} + static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) { return &container_of(ctrl->handler, struct ox05b1s, @@ -274,6 +416,12 @@ static int ox05b1s_s_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(sensor->regmap, OX05B1S_REG_EXPOSURE, ctrl->val, NULL); break; + case V4L2_CID_HDR_SENSOR_MODE: + if (sensor->model->set_hdr_mode) + ret = sensor->model->set_hdr_mode(sensor, ctrl->val); + else + ret = -EINVAL; + break; default: ret = -EINVAL; break; @@ -336,6 +484,13 @@ static int ox05b1s_init_controls(struct ox05b1s *sensor) ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 0, 0xFFFF, 1, 0x80); + if (sensor->model->hdr_modes) + ctrls->hdr_mode = v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_HDR_SENSOR_MODE, + sensor->model->hdr_modes_count - 1, + 0, 0, sensor->model->hdr_modes); + else + ctrls->hdr_mode = NULL; + if (hdl->error) { ret = hdl->error; goto free_ctrls; @@ -658,6 +813,8 @@ static u8 ox05b1s_code2dt(const u32 code) switch (code) { case MEDIA_BUS_FMT_Y10_1X10: return MIPI_CSI2_DT_RAW10; + case MEDIA_BUS_FMT_Y12_1X12: + return MIPI_CSI2_DT_RAW12; default: return MIPI_CSI2_DT_RAW10; } @@ -768,6 +925,9 @@ static int ox05b1s_read_chip_id(struct ox05b1s *sensor) } switch (chip_id) { + case 0x530841: + camera_name = "os08a20"; + break; case 0x580542: camera_name = "ox05b1s"; break; @@ -912,6 +1072,24 @@ static void ox05b1s_remove(struct i2c_client *client) static DEFINE_RUNTIME_DEV_PM_OPS(ox05b1s_pm_ops, ox05b1s_runtime_suspend, ox05b1s_runtime_resume, NULL); +static const struct ox05b1s_plat_data os08a20_data = { + .name = "os08a20", + .chip_id = 0x530841, + .native_width = 3872, /* 16 dummy + 3840 active + 16 dummy */ + .native_height = 2192, /* 16 dummy + 2160 active + 16 dummy */ + .active_top = 16, + .active_left = 16, + .active_width = 3840, + .active_height = 2160, + .supported_modes = os08a20_supported_modes, + .default_mode_index = 0, + .supported_codes = os08a20_supported_codes, + .hdr_modes = os08a20_hdr_modes, + .hdr_modes_count = ARRAY_SIZE(os08a20_hdr_modes), + .set_hdr_mode = os08a20_set_hdr_mode, + +}; + static const struct ox05b1s_plat_data ox05b1s_data = { .name = "ox05b1s", .chip_id = 0x580542, @@ -923,10 +1101,17 @@ static const struct ox05b1s_plat_data ox05b1s_data = { .active_height = 1944, .supported_modes = ox05b1s_supported_modes, .default_mode_index = 0, - .supported_codes = ox05b1s_supported_codes + .supported_codes = ox05b1s_supported_codes, + .hdr_modes = NULL, + .hdr_modes_count = 0, + .set_hdr_mode = NULL }; static const struct of_device_id ox05b1s_of_match[] = { + { + .compatible = "ovti,os08a20", + .data = &os08a20_data, + }, { .compatible = "ovti,ox05b1s", .data = &ox05b1s_data, diff --git a/drivers/media/i2c/ox05b1s/ox05b1s_modes.c b/drivers/media/i2c/ox05b1s/ox05b1s_modes.c index 9a1f3a89077c..e17c32a90cba 100644 --- a/drivers/media/i2c/ox05b1s/ox05b1s_modes.c +++ b/drivers/media/i2c/ox05b1s/ox05b1s_modes.c @@ -10,6 +10,116 @@ #include <media/v4l2-cci.h> #include "ox05b1s.h" +#define OS08A20_REG_MIPI_BIT_10_12 CCI_REG8(0x031e) +/* Analog Control Registers 0x3600-0x3637 */ +#define OS08A20_REG_ANA_CTRL CCI_REG8(0x3600) +#define OS08A20_REG_CORE_0 CCI_REG8(0x3660) +/* Sensor Timing Control Registers 0x3700-0x37ff */ +#define OS08A20_REG_SENSOR_TIMING_CTRL CCI_REG8(0x3700) +#define OS08A20_REG_X_ODD_INC CCI_REG8(0x3814) +#define OS08A20_REG_Y_ODD_INC CCI_REG8(0x3816) +#define OS08A20_REG_FORMAT1 CCI_REG8(0x3820) +#define OS08A20_REG_FORMAT2 CCI_REG8(0x3821) +#define OS08A20_REG_PCLK_PERIOD CCI_REG8(0x4837) +#define OS08A20_REG_ISP_CTRL_1 CCI_REG8(0x5001) +#define OS08A20_REG_ISP_CTRL_5 CCI_REG8(0x5005) + +/* Common register configuration for Omnivision OS08A20 raw camera */ +static const struct ox05b1s_reg os08a20_init_setting_common[] = { + { OS08A20_REG_ANA_CTRL + 0x05, 0x50 }, + { OS08A20_REG_ANA_CTRL + 0x10, 0x39 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x5e, 0x0b }, + { OS08A20_REG_ISP_CTRL_1, 0x42 }, + { OS08A20_REG_ISP_CTRL_5, 0x00 }, + { /* sentinel*/ } +}; + +/* Common register configuration for Omnivision OS08A20 10 bit */ +static const struct ox05b1s_reg os08a20_init_setting_10bit[] = { + { OS08A20_REG_MIPI_BIT_10_12, 0x09 }, + { OS08A20_REG_ANA_CTRL + 0x09, 0xb5 }, + { OS08A20_REG_CORE_0, 0x43 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x06, 0x35 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x0a, 0x00 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x0b, 0x98 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x09, 0x49 }, + { /* sentinel*/ } +}; + +/* Common register configuration for Omnivision OS08A20 12 bit */ +static const struct ox05b1s_reg os08a20_init_setting_12bit[] = { + { OS08A20_REG_MIPI_BIT_10_12, 0x0a }, + { OS08A20_REG_ANA_CTRL + 0x09, 0xdb }, + { OS08A20_REG_CORE_0, 0xd3 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x06, 0x6a }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x0a, 0x01 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x0b, 0x30 }, + { OS08A20_REG_SENSOR_TIMING_CTRL + 0x09, 0x48 }, + { /* sentinel*/ } +}; + +/* Mode specific register configurations for Omnivision OS08A20 raw camera */ + +/* OS08A20 3840 x 2160 @30fps BGGR10 no more HDR */ +static const struct ox05b1s_reg os08a20_init_setting_4k_10b[] = { + { OS08A20_REG_FORMAT2, 0x04 }, /* mirror */ + { OS08A20_REG_PCLK_PERIOD, 0x10 }, + { /* sentinel*/ } +}; + +/* OS08A20 3840 x 2160 @30fps BGGR12 */ +static const struct ox05b1s_reg os08a20_init_setting_4k_12b[] = { + { OS08A20_REG_FORMAT2, 0x04 }, /* mirror */ + { OS08A20_REG_PCLK_PERIOD, 0x10 }, + { /* sentinel*/ } +}; + +/* OS08A20 1920 x 1080 @60fps BGGR10 */ +static const struct ox05b1s_reg os08a20_init_setting_1080p_10b[] = { + { OS08A20_REG_X_ODD_INC, 0x03 }, + { OS08A20_REG_Y_ODD_INC, 0x03 }, + { OS08A20_REG_FORMAT1, 0x01 }, /* vertical bining */ + { OS08A20_REG_FORMAT2, 0x05 }, /* mirror, horizontal bining */ + { OS08A20_REG_PCLK_PERIOD, 0x16 }, + { /* sentinel*/ } +}; + +const struct ox05b1s_reglist os08a20_reglist_4k_10b[] = { + { + .regs = os08a20_init_setting_common + }, { + .regs = os08a20_init_setting_10bit + }, { + .regs = os08a20_init_setting_4k_10b + }, { + /* sentinel */ + } +}; + +const struct ox05b1s_reglist os08a20_reglist_4k_12b[] = { + { + .regs = os08a20_init_setting_common + }, { + .regs = os08a20_init_setting_12bit + }, { + .regs = os08a20_init_setting_4k_12b + }, { + /* sentinel */ + } +}; + +const struct ox05b1s_reglist os08a20_reglist_1080p_10b[] = { + { + .regs = os08a20_init_setting_common + }, { + .regs = os08a20_init_setting_10bit + }, { + .regs = os08a20_init_setting_1080p_10b + }, { + /* sentinel */ + } +}; + #define OX05B1S_REG_PLL1_CTRL_REG07 CCI_REG8(0x0307) #define OX05B1S_REG_PLL3_CTRL_REG4A CCI_REG8(0x034a) #define OX05B1S_REG_PLL_MONITOR_REG0B CCI_REG8(0x040b) -- 2.25.1