diff --git a/drivers/media/i2c/sc450ai.c b/drivers/media/i2c/sc450ai.c index 59273dba8f63..17794f7ed050 100644 --- a/drivers/media/i2c/sc450ai.c +++ b/drivers/media/i2c/sc450ai.c @@ -7,6 +7,7 @@ * V0.0X01.0X01 first version * V0.0X01.0X02 Increase vblank in 2688x1520@30fps linear 4lane configuration * V0.0X01.0X03 Add sc450ai 2lane hdr/linear configuration and 4 lane linear configuration + * V0.0X01.0X04 Add sc450ai thunder boot support */ //#define DEBUG @@ -34,28 +35,35 @@ #include "cam-tb-setup.h" #include "cam-sleep-wakeup.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x04) #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif -#define SC450AI_LANES_2LANE 2 -#define SC450AI_LANES_4LANE 4 #define SC450AI_BITS_PER_SAMPLE 10 #define SC450AI_LINK_FREQ_180 180000000 #define SC450AI_LINK_FREQ_360 360000000 #define SC450AI_LINK_FREQ_540 540000000 #define SC450AI_MAX_LINK_FREQ SC450AI_LINK_FREQ_540 -#define PIXEL_RATE_WITH_360M_10BIT (SC450AI_LINK_FREQ_360 / SC450AI_BITS_PER_SAMPLE * 2 * \ - SC450AI_LANES_2LANE) +/* 2 lane */ +#define PIXEL_RATE_WITH_360M_10BIT_2L (SC450AI_LINK_FREQ_360 * 2 * \ + 2 / SC450AI_BITS_PER_SAMPLE) +/* 4 lane */ +#define PIXEL_RATE_WITH_180M_10BIT_4L (SC450AI_LINK_FREQ_180 * 2 / \ + SC450AI_BITS_PER_SAMPLE * 4) +#define PIXEL_RATE_WITH_360M_10BIT_4L (SC450AI_LINK_FREQ_360 * 2 / \ + SC450AI_BITS_PER_SAMPLE * 4) #define SC450AI_XVCLK_FREQ 27000000 #define CHIP_ID 0xbd2f #define SC450AI_REG_CHIP_ID 0x3107 +#define SC450AI_REG_MIPI_CTRL 0x3019 +#define SC450AI_MIPI_CTRL_ON 0x00 +#define SC450AI_MIPI_CTRL_OFF 0xff #define SC450AI_REG_CTRL_MODE 0x0100 #define SC450AI_MODE_SW_STANDBY 0x0 #define SC450AI_MODE_STREAMING BIT(0) @@ -63,9 +71,9 @@ #define SC450AI_REG_EXPOSURE_H 0x3e00 #define SC450AI_REG_EXPOSURE_M 0x3e01 #define SC450AI_REG_EXPOSURE_L 0x3e02 -#define SC450AI_REG_EXPOSURE_SHORT_H 0x3e22 -#define SC450AI_REG_EXPOSURE_SHORT_M 0x3e04 -#define SC450AI_REG_EXPOSURE_SHORT_L 0x3e05 +#define SC450AI_REG_SEXPOSURE_H 0x3e22 +#define SC450AI_REG_SEXPOSURE_M 0x3e04 +#define SC450AI_REG_SEXPOSURE_L 0x3e05 #define SC450AI_EXPOSURE_MIN 1 #define SC450AI_EXPOSURE_STEP 1 #define SC450AI_VTS_MAX 0x7fff @@ -74,20 +82,22 @@ #define SC450AI_REG_DIG_FINE_GAIN 0x3e07 #define SC450AI_REG_ANA_GAIN 0x3e08 #define SC450AI_REG_ANA_FINE_GAIN 0x3e09 -#define SC450AI_REG_DIG_GAIN_SHORT 0x3e10 -#define SC450AI_REG_DIG_FINE_GAIN_SHORT 0x3e11 -#define SC450AI_REG_ANA_GAIN_SHORT 0x3e12 -#define SC450AI_REG_ANA_FINE_GAIN_SHORT 0x3e13 +#define SC450AI_REG_SDIG_GAIN 0x3e10 +#define SC450AI_REG_SDIG_FINE_GAIN 0x3e11 +#define SC450AI_REG_SANA_GAIN 0x3e12 +#define SC450AI_REG_SANA_FINE_GAIN 0x3e13 #define SC450AI_GAIN_MIN 0x40 //0x0080 #define SC450AI_GAIN_MAX 61975 //60.523*16*64 (99614) //48.64*16*128 #define SC450AI_GAIN_STEP 1 #define SC450AI_GAIN_DEFAULT 0x40 //0x80 // Note that the benchmark is 0x40 +#define SC450AI_LGAIN 0 +#define SC450AI_SGAIN 1 #define SC450AI_REG_GROUP_HOLD 0x3800//0x3812 #define SC450AI_GROUP_HOLD_START 0x00 #define SC450AI_GROUP_HOLD_END 0x30 // Not used -#define SC450AI_REG_TEST_PATTERN 0x4501 +#define SC450AI_REG_TEST_PATTERN 0x4501 #define SC450AI_TEST_PATTERN_BIT_MASK BIT(3) #define SC450AI_REG_VTS_H 0x320e @@ -95,9 +105,9 @@ #define SC450AI_FLIP_MIRROR_REG 0x3221 -#define SC450AI_FETCH_EXP_H(VAL) (((VAL) >> 12) & 0xF) -#define SC450AI_FETCH_EXP_M(VAL) (((VAL) >> 4) & 0xFF) -#define SC450AI_FETCH_EXP_L(VAL) (((VAL) & 0xF) << 4) +#define SC450AI_FETCH_EXP_H(VAL) (((VAL) >> 12) & 0xF) +#define SC450AI_FETCH_EXP_M(VAL) (((VAL) >> 4) & 0xFF) +#define SC450AI_FETCH_EXP_L(VAL) (((VAL) & 0xF) << 4) #define SC450AI_FETCH_MIRROR(VAL, ENABLE) (ENABLE ? VAL | 0x06 : VAL & 0xf9) #define SC450AI_FETCH_FLIP(VAL, ENABLE) (ENABLE ? VAL | 0x60 : VAL & 0x9f) @@ -135,11 +145,13 @@ struct sc450ai_mode { u32 hts_def; u32 vts_def; u32 exp_def; + const struct regval *global_reg_list; const struct regval *reg_list; u32 hdr_mode; - u32 xvclk_freq; + u32 mclk; u32 link_freq_idx; u32 vc[PAD_MAX]; + u8 bpp; u32 lanes; }; @@ -169,20 +181,22 @@ struct sc450ai { struct v4l2_fract cur_fps; bool streaming; bool power_on; + const struct sc450ai_mode *supported_modes; const struct sc450ai_mode *cur_mode; + u32 cfg_num; u32 module_index; const char *module_facing; const char *module_name; const char *len_name; + u32 standby_hw; u32 cur_vts; bool has_init_exp; bool is_thunderboot; bool is_first_streamoff; + bool is_standby; struct preisp_hdrae_exp_s init_hdrae_exp; struct cam_sw_info *cam_sw_inf; struct v4l2_fwnode_endpoint bus_cfg; - const struct sc450ai_mode *supported_modes; - u32 cfg_num; }; #define to_sc450ai(sd) container_of(sd, struct sc450ai, subdev) @@ -190,10 +204,9 @@ struct sc450ai { /* * Xclk 24Mhz */ -static const struct regval sc450ai_global_regs[] = { +static const struct regval sc450ai_global_regs_2lane[] = { {REG_NULL, 0x00}, }; - /* * Xclk 27Mhz * max_framerate 120fps @@ -790,6 +803,10 @@ static const struct regval sc450ai_hdr2_10_2688x1520_25fps_2lane_regs[] = { {REG_NULL, 0x00}, }; +static const struct regval sc450ai_global_4lane_regs[] = { + {REG_NULL, 0x00}, +}; + /* * Xclk 27Mhz * max_framerate 30fps @@ -1202,8 +1219,9 @@ static const struct sc450ai_mode supported_modes_2lane[] = { .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, .reg_list = sc450ai_linear_10_2688x1520_30fps_2lane_regs, .hdr_mode = NO_HDR, - .xvclk_freq = 27000000, + .mclk = 27000000, .link_freq_idx = 1, + .bpp = 10, .vc[PAD0] = 0, .lanes = 2, }, @@ -1218,10 +1236,12 @@ static const struct sc450ai_mode supported_modes_2lane[] = { .hts_def = 0x39e * 4, .vts_def = 0x0c26, .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .global_reg_list = sc450ai_global_regs_2lane, .reg_list = sc450ai_hdr2_10_2688x1520_25fps_2lane_regs, .hdr_mode = HDR_X2, - .xvclk_freq = 27000000, + .mclk = 27000000, .link_freq_idx = 2, + .bpp = 10, .vc[PAD0] = 1, .vc[PAD1] = 0,//L->CSI_WR0 .vc[PAD2] = 1, @@ -1239,10 +1259,12 @@ static const struct sc450ai_mode supported_modes_2lane[] = { .hts_def = 0x03a8, .vts_def = 0x030c, .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .global_reg_list = sc450ai_global_regs_2lane, .reg_list = sc450ai_linear_10_1344x760_120fps_2lane_regs, .hdr_mode = NO_HDR, - .xvclk_freq = 27000000, + .mclk = 27000000, .link_freq_idx = 1, + .bpp = 10, .vc[PAD0] = 0, .lanes = 2, }, @@ -1260,10 +1282,12 @@ static const struct sc450ai_mode supported_modes_4lane[] = { .hts_def = 0x2ee * 4, .vts_def = 0x0c30, .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .global_reg_list = sc450ai_global_4lane_regs, .reg_list = sc450ai_linear_10_2688x1520_30fps_4lane_regs, .hdr_mode = NO_HDR, - .xvclk_freq = 27000000, + .mclk = 27000000, .link_freq_idx = 0, + .bpp = 10, .vc[PAD0] = 0, .lanes = 4, }, @@ -1278,10 +1302,12 @@ static const struct sc450ai_mode supported_modes_4lane[] = { .hts_def = 0x3a8 * 4, .vts_def = 0x0c30, .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .global_reg_list = sc450ai_global_4lane_regs, .reg_list = sc450ai_hdr2_10_2688x1520_30fps_4lane_regs, .hdr_mode = HDR_X2, - .xvclk_freq = 27000000, + .mclk = 27000000, .link_freq_idx = 1, + .bpp = 10, .vc[PAD0] = 1, .vc[PAD1] = 0,//L->CSI_WR0 .vc[PAD2] = 1, @@ -1290,6 +1316,10 @@ static const struct sc450ai_mode supported_modes_4lane[] = { }, }; +static const u32 bus_code[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + static const s64 link_freq_menu_items[] = { SC450AI_LINK_FREQ_180, SC450AI_LINK_FREQ_360, @@ -1380,7 +1410,7 @@ static int sc450ai_read_reg(struct i2c_client *client, u16 reg, unsigned int len return 0; } -static int sc450ai_set_gain_reg(struct sc450ai *sc450ai, u32 gain) +static int sc450ai_set_gain_reg(struct sc450ai *sc450ai, u32 gain, int mode) { struct i2c_client *client = sc450ai->client; u32 coarse_again = 0, coarse_dgain = 0, fine_again = 0, fine_dgain = 0; @@ -1447,38 +1477,38 @@ static int sc450ai_set_gain_reg(struct sc450ai *sc450ai, u32 gain) dev_dbg(&client->dev, "c_again: 0x%x, c_dgain: 0x%x, f_again: 0x%x, f_dgain: 0x%0x\n", coarse_again, coarse_dgain, fine_again, fine_dgain); - ret = sc450ai_write_reg(sc450ai->client, - SC450AI_REG_DIG_GAIN, - SC450AI_REG_VALUE_08BIT, - coarse_dgain); - ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_DIG_FINE_GAIN, - SC450AI_REG_VALUE_08BIT, - fine_dgain); - ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_ANA_GAIN, - SC450AI_REG_VALUE_08BIT, - coarse_again); - ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_ANA_FINE_GAIN, - SC450AI_REG_VALUE_08BIT, - fine_again); - - if (sc450ai->cur_mode->hdr_mode == HDR_X2) { - ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_DIG_GAIN_SHORT, + if (mode == SC450AI_LGAIN) { + ret = sc450ai_write_reg(sc450ai->client, + SC450AI_REG_DIG_GAIN, SC450AI_REG_VALUE_08BIT, coarse_dgain); ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_DIG_FINE_GAIN_SHORT, + SC450AI_REG_DIG_FINE_GAIN, + SC450AI_REG_VALUE_08BIT, + fine_dgain); + ret |= sc450ai_write_reg(sc450ai->client, + SC450AI_REG_ANA_GAIN, + SC450AI_REG_VALUE_08BIT, + coarse_again); + ret |= sc450ai_write_reg(sc450ai->client, + SC450AI_REG_ANA_FINE_GAIN, + SC450AI_REG_VALUE_08BIT, + fine_again); + } else { + ret |= sc450ai_write_reg(sc450ai->client, + SC450AI_REG_SDIG_GAIN, + SC450AI_REG_VALUE_08BIT, + coarse_dgain); + ret |= sc450ai_write_reg(sc450ai->client, + SC450AI_REG_SDIG_FINE_GAIN, SC450AI_REG_VALUE_08BIT, fine_dgain); ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_ANA_GAIN_SHORT, + SC450AI_REG_SANA_GAIN, SC450AI_REG_VALUE_08BIT, coarse_again); ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_ANA_FINE_GAIN_SHORT, + SC450AI_REG_SANA_FINE_GAIN, SC450AI_REG_VALUE_08BIT, fine_again); } @@ -1486,199 +1516,8 @@ static int sc450ai_set_gain_reg(struct sc450ai *sc450ai, u32 gain) return ret; } -static int sc450ai_get_reso_dist(const struct sc450ai_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -static const struct sc450ai_mode * -sc450ai_find_best_fit(struct sc450ai *sc450ai, struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - unsigned int i; - - for (i = 0; i < sc450ai->cfg_num; i++) { - dist = sc450ai_get_reso_dist(&sc450ai->supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &sc450ai->supported_modes[cur_best_fit]; -} - -static int sc450ai_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - const struct sc450ai_mode *mode; - s64 h_blank, vblank_def; - u64 dst_link_freq = 0; - u64 dst_pixel_rate = 0; - - mutex_lock(&sc450ai->mutex); - - mode = sc450ai_find_best_fit(sc450ai, fmt); - fmt->format.code = mode->bus_fmt; - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; -#else - mutex_unlock(&sc450ai->mutex); - return -ENOTTY; -#endif - } else { - sc450ai->cur_mode = mode; - h_blank = mode->hts_def - mode->width; - __v4l2_ctrl_modify_range(sc450ai->hblank, h_blank, - h_blank, 1, h_blank); - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(sc450ai->vblank, vblank_def, - SC450AI_VTS_MAX - mode->height, - 1, vblank_def); - dst_link_freq = mode->link_freq_idx; - dst_pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / - SC450AI_BITS_PER_SAMPLE * 2 * mode->lanes; - __v4l2_ctrl_s_ctrl_int64(sc450ai->pixel_rate, - dst_pixel_rate); - __v4l2_ctrl_s_ctrl(sc450ai->link_freq, - dst_link_freq); - sc450ai->cur_fps = mode->max_fps; - } - - mutex_unlock(&sc450ai->mutex); - - return 0; -} - -static int sc450ai_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - const struct sc450ai_mode *mode = sc450ai->cur_mode; - - mutex_lock(&sc450ai->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); -#else - mutex_unlock(&sc450ai->mutex); - return -ENOTTY; -#endif - } else { - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.code = mode->bus_fmt; - fmt->format.field = V4L2_FIELD_NONE; - /* format info: width/height/data type/virctual channel */ - if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) - fmt->reserved[0] = mode->vc[fmt->pad]; - else - fmt->reserved[0] = mode->vc[PAD0]; - } - mutex_unlock(&sc450ai->mutex); - - return 0; -} - -static int sc450ai_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - - if (code->index != 0) - return -EINVAL; - code->code = sc450ai->cur_mode->bus_fmt; - - return 0; -} - -static int sc450ai_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - - if (fse->index >= sc450ai->cfg_num) - return -EINVAL; - - if (fse->code != sc450ai->supported_modes[0].bus_fmt) - return -EINVAL; - - fse->min_width = sc450ai->supported_modes[fse->index].width; - fse->max_width = sc450ai->supported_modes[fse->index].width; - fse->max_height = sc450ai->supported_modes[fse->index].height; - fse->min_height = sc450ai->supported_modes[fse->index].height; - - return 0; -} - -static int sc450ai_enable_test_pattern(struct sc450ai *sc450ai, u32 pattern) -{ - u32 val = 0; - int ret = 0; - - ret = sc450ai_read_reg(sc450ai->client, SC450AI_REG_TEST_PATTERN, - SC450AI_REG_VALUE_08BIT, &val); - if (pattern) - val |= SC450AI_TEST_PATTERN_BIT_MASK; - else - val &= ~SC450AI_TEST_PATTERN_BIT_MASK; - - ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_TEST_PATTERN, - SC450AI_REG_VALUE_08BIT, val); - return ret; -} - -static int sc450ai_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - const struct sc450ai_mode *mode = sc450ai->cur_mode; - - if (sc450ai->streaming) - fi->interval = sc450ai->cur_fps; - else - fi->interval = mode->max_fps; - return 0; -} - -static int sc450ai_g_mbus_config(struct v4l2_subdev *sd, - unsigned int pad_id, - struct v4l2_mbus_config *config) -{ - struct sc450ai *sc450ai = to_sc450ai(sd); - - config->type = V4L2_MBUS_CSI2_DPHY; - config->bus.mipi_csi2.num_data_lanes = sc450ai->cur_mode->lanes; - - return 0; -} - -static void sc450ai_get_module_inf(struct sc450ai *sc450ai, - struct rkmodule_inf *inf) -{ - memset(inf, 0, sizeof(*inf)); - strscpy(inf->base.sensor, SC450AI_NAME, sizeof(inf->base.sensor)); - strscpy(inf->base.module, sc450ai->module_name, - sizeof(inf->base.module)); - strscpy(inf->base.lens, sc450ai->len_name, sizeof(inf->base.lens)); -} - static int sc450ai_set_hdrae(struct sc450ai *sc450ai, - struct preisp_hdrae_exp_s *ae) + struct preisp_hdrae_exp_s *ae) { int ret = 0; u32 l_exp_time, m_exp_time, s_exp_time; @@ -1728,31 +1567,364 @@ static int sc450ai_set_hdrae(struct sc450ai *sc450ai, SC450AI_REG_EXPOSURE_L, SC450AI_REG_VALUE_08BIT, SC450AI_FETCH_EXP_L(l_exp_time)); - ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_EXPOSURE_SHORT_M, + SC450AI_REG_SEXPOSURE_M, SC450AI_REG_VALUE_08BIT, SC450AI_FETCH_EXP_M(s_exp_time)); ret |= sc450ai_write_reg(sc450ai->client, - SC450AI_REG_EXPOSURE_SHORT_L, + SC450AI_REG_SEXPOSURE_L, SC450AI_REG_VALUE_08BIT, SC450AI_FETCH_EXP_L(s_exp_time)); - ret |= sc450ai_set_gain_reg(sc450ai, l_a_gain); + ret |= sc450ai_set_gain_reg(sc450ai, l_a_gain, SC450AI_LGAIN); + ret |= sc450ai_set_gain_reg(sc450ai, s_a_gain, SC450AI_SGAIN); return ret; } +static int sc450ai_get_reso_dist(const struct sc450ai_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct sc450ai_mode * +sc450ai_find_best_fit(struct sc450ai *sc450ai, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < sc450ai->cfg_num; i++) { + dist = sc450ai_get_reso_dist(&sc450ai->supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } else if (dist == cur_best_fit_dist && + framefmt->code == sc450ai->supported_modes[i].bus_fmt) { + cur_best_fit = i; + break; + } + } + + return &sc450ai->supported_modes[cur_best_fit]; +} + +static int sc450ai_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + const struct sc450ai_mode *mode; + s64 h_blank, vblank_def; + u64 dst_link_freq = 0; + u64 dst_pixel_rate = 0; + u8 lanes = sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes; + + mutex_lock(&sc450ai->mutex); + + mode = sc450ai_find_best_fit(sc450ai, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&sc450ai->mutex); + return -ENOTTY; +#endif + } else { + sc450ai->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(sc450ai->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc450ai->vblank, vblank_def, + SC450AI_VTS_MAX - mode->height, + 1, vblank_def); + dst_link_freq = mode->link_freq_idx; + dst_pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / + mode->bpp * 2 * lanes; + __v4l2_ctrl_s_ctrl_int64(sc450ai->pixel_rate, + dst_pixel_rate); + __v4l2_ctrl_s_ctrl(sc450ai->link_freq, + dst_link_freq); + sc450ai->cur_fps = mode->max_fps; + } + + mutex_unlock(&sc450ai->mutex); + + return 0; +} + +static int sc450ai_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + const struct sc450ai_mode *mode = sc450ai->cur_mode; + + mutex_lock(&sc450ai->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&sc450ai->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&sc450ai->mutex); + + return 0; +} + +static int sc450ai_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(bus_code)) + return -EINVAL; + code->code = bus_code[code->index]; + + return 0; +} + +static int sc450ai_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + + if (fse->index >= sc450ai->cfg_num) + return -EINVAL; + + if (fse->code != sc450ai->supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = sc450ai->supported_modes[fse->index].width; + fse->max_width = sc450ai->supported_modes[fse->index].width; + fse->max_height = sc450ai->supported_modes[fse->index].height; + fse->min_height = sc450ai->supported_modes[fse->index].height; + + return 0; +} + +static int sc450ai_enable_test_pattern(struct sc450ai *sc450ai, u32 pattern) +{ + u32 val = 0; + int ret = 0; + + ret = sc450ai_read_reg(sc450ai->client, SC450AI_REG_TEST_PATTERN, + SC450AI_REG_VALUE_08BIT, &val); + if (pattern) + val |= SC450AI_TEST_PATTERN_BIT_MASK; + else + val &= ~SC450AI_TEST_PATTERN_BIT_MASK; + + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_TEST_PATTERN, + SC450AI_REG_VALUE_08BIT, val); + return ret; +} + +static int sc450ai_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + const struct sc450ai_mode *mode = sc450ai->cur_mode; + + if (sc450ai->streaming) + fi->interval = sc450ai->cur_fps; + else + fi->interval = mode->max_fps; + return 0; +} + +static const struct sc450ai_mode *sc450ai_find_mode(struct sc450ai *sc450ai, int fps) +{ + const struct sc450ai_mode *mode = NULL; + const struct sc450ai_mode *match = NULL; + int cur_fps = 0; + int i = 0; + + for (i = 0; i < sc450ai->cfg_num; i++) { + mode = &sc450ai->supported_modes[i]; + if (mode->width == sc450ai->cur_mode->width && + mode->height == sc450ai->cur_mode->height && + mode->hdr_mode == sc450ai->cur_mode->hdr_mode && + mode->bus_fmt == sc450ai->cur_mode->bus_fmt) { + cur_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + if (cur_fps == fps) { + match = mode; + break; + } + } + } + return match; +} + +static int sc450ai_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + const struct sc450ai_mode *mode = NULL; + struct v4l2_fract *fract = &fi->interval; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + int fps; + + if (sc450ai->streaming) + return -EBUSY; + + if (fi->pad != 0) + return -EINVAL; + + if (fract->numerator == 0) { + v4l2_err(sd, "error param, check interval param\n"); + return -EINVAL; + } + fps = DIV_ROUND_CLOSEST(fract->denominator, fract->numerator); + mode = sc450ai_find_mode(sc450ai, fps); + if (mode == NULL) { + v4l2_err(sd, "couldn't match fi\n"); + return -EINVAL; + } + + sc450ai->cur_mode = mode; + + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(sc450ai->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc450ai->vblank, vblank_def, + SC450AI_VTS_MAX - mode->height, + 1, vblank_def); + pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / + mode->bpp * 2 * mode->lanes; + + __v4l2_ctrl_s_ctrl_int64(sc450ai->pixel_rate, + pixel_rate); + __v4l2_ctrl_s_ctrl(sc450ai->link_freq, + mode->link_freq_idx); + sc450ai->cur_fps = mode->max_fps; + + return 0; +} + +static int sc450ai_g_mbus_config(struct v4l2_subdev *sd, + unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct sc450ai *sc450ai = to_sc450ai(sd); + + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = sc450ai->cur_mode->lanes; + + return 0; +} + +static void sc450ai_get_module_inf(struct sc450ai *sc450ai, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, SC450AI_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, sc450ai->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, sc450ai->len_name, sizeof(inf->base.lens)); +} + +static int sc450ai_set_setting(struct sc450ai *sc450ai, struct rk_sensor_setting *setting) +{ + int i = 0; + int cur_fps = 0; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + const struct sc450ai_mode *mode = NULL; + const struct sc450ai_mode *match = NULL; + u8 lane = sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes; + + dev_info(&sc450ai->client->dev, + "sensor setting: %d x %d, fps:%d fmt:%d, mode:%d\n", + setting->width, setting->height, + setting->fps, setting->fmt, setting->mode); + + for (i = 0; i < sc450ai->cfg_num; i++) { + mode = &sc450ai->supported_modes[i]; + if (mode->width == setting->width && + mode->height == setting->height && + mode->hdr_mode == setting->mode && + mode->bus_fmt == setting->fmt) { + cur_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + if (cur_fps == setting->fps) { + match = mode; + break; + } + } + } + + if (match) { + dev_info(&sc450ai->client->dev, "-----%s: match the support mode, mode idx:%d-----\n", + __func__, i); + sc450ai->cur_mode = mode; + + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(sc450ai->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc450ai->vblank, vblank_def, + SC450AI_VTS_MAX - mode->height, + 1, vblank_def); + + + __v4l2_ctrl_s_ctrl(sc450ai->link_freq, mode->link_freq_idx); + pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / + mode->bpp * 2 * lane; + __v4l2_ctrl_s_ctrl_int64(sc450ai->pixel_rate, pixel_rate); + dev_info(&sc450ai->client->dev, "freq_idx:%d pixel_rate:%lld\n", + mode->link_freq_idx, pixel_rate); + + sc450ai->cur_vts = mode->vts_def; + sc450ai->cur_fps = mode->max_fps; + + dev_info(&sc450ai->client->dev, "hts_def:%d cur_vts:%d cur_fps:%d\n", + mode->hts_def, mode->vts_def, + sc450ai->cur_fps.denominator / sc450ai->cur_fps.numerator); + } else { + dev_err(&sc450ai->client->dev, "couldn't match the support modes\n"); + return -EINVAL; + } + + return 0; +} + static long sc450ai_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct sc450ai *sc450ai = to_sc450ai(sd); struct rkmodule_hdr_cfg *hdr; + struct rk_sensor_setting *setting; u32 i, h, w; long ret = 0; u32 stream = 0; - const struct sc450ai_mode *mode; u64 dst_link_freq = 0; u64 dst_pixel_rate = 0; - + u8 lanes = sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes; + const struct sc450ai_mode *mode; + int cur_best_fit = -1; + int cur_best_fit_dist = -1; + int cur_dist, cur_fps, dst_fps; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1765,52 +1937,172 @@ static long sc450ai_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) break; case RKMODULE_SET_HDR_CFG: hdr = (struct rkmodule_hdr_cfg *)arg; + if (hdr->hdr_mode == sc450ai->cur_mode->hdr_mode) + return 0; w = sc450ai->cur_mode->width; h = sc450ai->cur_mode->height; + dst_fps = DIV_ROUND_CLOSEST(sc450ai->cur_mode->max_fps.denominator, + sc450ai->cur_mode->max_fps.numerator); for (i = 0; i < sc450ai->cfg_num; i++) { if (w == sc450ai->supported_modes[i].width && h == sc450ai->supported_modes[i].height && - sc450ai->supported_modes[i].hdr_mode == hdr->hdr_mode) { - sc450ai->cur_mode = &sc450ai->supported_modes[i]; - break; + sc450ai->supported_modes[i].hdr_mode == hdr->hdr_mode && + sc450ai->supported_modes[i].bus_fmt == sc450ai->cur_mode->bus_fmt) { + cur_fps = DIV_ROUND_CLOSEST(sc450ai->supported_modes[i].max_fps.denominator, + sc450ai->supported_modes[i].max_fps.numerator); + cur_dist = abs(cur_fps - dst_fps); + if (cur_best_fit_dist == -1 || cur_dist < cur_best_fit_dist) { + cur_best_fit_dist = cur_dist; + cur_best_fit = i; + } else if (cur_dist == cur_best_fit_dist) { + cur_best_fit = i; + break; + } } } - if (i == sc450ai->cfg_num) { + if (cur_best_fit == -1) { dev_err(&sc450ai->client->dev, "not find hdr mode:%d %dx%d config\n", hdr->hdr_mode, w, h); ret = -EINVAL; } else { + sc450ai->cur_mode = &sc450ai->supported_modes[cur_best_fit]; mode = sc450ai->cur_mode; w = mode->hts_def - mode->width; h = mode->vts_def - mode->height; __v4l2_ctrl_modify_range(sc450ai->hblank, w, w, 1, w); __v4l2_ctrl_modify_range(sc450ai->vblank, h, - SC450AI_VTS_MAX - mode->height, 1, h); + SC450AI_VTS_MAX - sc450ai->cur_mode->height, 1, h); + sc450ai->cur_fps = sc450ai->cur_mode->max_fps; + dst_link_freq = mode->link_freq_idx; dst_pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / - SC450AI_BITS_PER_SAMPLE * 2 * mode->lanes; + mode->bpp * 2 * lanes; __v4l2_ctrl_s_ctrl_int64(sc450ai->pixel_rate, dst_pixel_rate); __v4l2_ctrl_s_ctrl(sc450ai->link_freq, dst_link_freq); - sc450ai->cur_fps = mode->max_fps; } break; case PREISP_CMD_SET_HDRAE_EXP: - ret = sc450ai_set_hdrae(sc450ai, arg); + sc450ai_set_hdrae(sc450ai, arg); + if (sc450ai->cam_sw_inf) + memcpy(&sc450ai->cam_sw_inf->hdr_ae, (struct preisp_hdrae_exp_s *)(arg), + sizeof(struct preisp_hdrae_exp_s)); break; case RKMODULE_SET_QUICK_STREAM: stream = *((u32 *)arg); - if (stream) - ret = sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, - SC450AI_REG_VALUE_08BIT, SC450AI_MODE_STREAMING); - else - ret = sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, - SC450AI_REG_VALUE_08BIT, SC450AI_MODE_SW_STANDBY); + + if (sc450ai->standby_hw) { /* hardware standby */ + if (stream) { + u32 val; + + /* pwdn gpio pull up */ + if (!IS_ERR(sc450ai->pwdn_gpio)) + gpiod_set_value_cansleep(sc450ai->pwdn_gpio, 1); + // Make sure __v4l2_ctrl_handler_setup can be called correctly + sc450ai->is_standby = false; + /* mipi clk on */ + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_MIPI_CTRL, + SC450AI_REG_VALUE_08BIT, + SC450AI_MIPI_CTRL_ON); + /* adjust timing */ + ret |= sc450ai_read_reg(sc450ai->client, 0x36e9, + SC450AI_REG_VALUE_08BIT, &val); + val &= 0x7f; + ret |= sc450ai_write_reg(sc450ai->client, 0x36e9, + SC450AI_REG_VALUE_08BIT, + val); + ret |= sc450ai_read_reg(sc450ai->client, 0x36f9, + SC450AI_REG_VALUE_08BIT, &val); + val &= 0x7f; + ret |= sc450ai_write_reg(sc450ai->client, 0x36f9, + SC450AI_REG_VALUE_08BIT, + val); + + #if IS_REACHABLE(CONFIG_VIDEO_CAM_SLEEP_WAKEUP) + if (__v4l2_ctrl_handler_setup(&sc450ai->ctrl_handler)) + dev_err(&sc450ai->client->dev, "__v4l2_ctrl_handler_setup fail!"); + if (sc450ai->cur_mode->hdr_mode != NO_HDR) { // hdr mode + if (sc450ai->cam_sw_inf) { + ret = sc450ai_ioctl(&sc450ai->subdev, + PREISP_CMD_SET_HDRAE_EXP, + &sc450ai->cam_sw_inf->hdr_ae); + if (ret) { + dev_err(&sc450ai->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + } + #endif + + /* stream on */ + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, + SC450AI_REG_VALUE_08BIT, + SC450AI_MODE_STREAMING); + dev_info(&sc450ai->client->dev, + "quickstream, streaming on: exit hw standby mode\n"); + } else { + u32 val; + + /* adjust timing */ + ret |= sc450ai_read_reg(sc450ai->client, 0x36e9, + SC450AI_REG_VALUE_08BIT, &val); + val |= 0x80; + ret |= sc450ai_write_reg(sc450ai->client, 0x36e9, + SC450AI_REG_VALUE_08BIT, + val); + ret |= sc450ai_read_reg(sc450ai->client, 0x36f9, + SC450AI_REG_VALUE_08BIT, &val); + val |= 0x80; + ret |= sc450ai_write_reg(sc450ai->client, 0x36f9, + SC450AI_REG_VALUE_08BIT, + val); + /* stream off */ + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, + SC450AI_REG_VALUE_08BIT, + SC450AI_MODE_SW_STANDBY); + /* mipi clk off */ + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_MIPI_CTRL, + SC450AI_REG_VALUE_08BIT, + SC450AI_MIPI_CTRL_OFF); + /* pwnd gpio pull down */ + if (!IS_ERR(sc450ai->pwdn_gpio)) + gpiod_set_value_cansleep(sc450ai->pwdn_gpio, 0); + dev_info(&sc450ai->client->dev, + "quickstream, streaming off: enter hw standby mode\n"); + sc450ai->is_standby = true; + } + } else { /* software standby */ + if (stream) { + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_MIPI_CTRL, + SC450AI_REG_VALUE_08BIT, + SC450AI_MIPI_CTRL_ON); + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, + SC450AI_REG_VALUE_08BIT, + SC450AI_MODE_STREAMING); + dev_info(&sc450ai->client->dev, + "quickstream, streaming on: exit soft standby mode\n"); + } else { + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_CTRL_MODE, + SC450AI_REG_VALUE_08BIT, + SC450AI_MODE_SW_STANDBY); + ret |= sc450ai_write_reg(sc450ai->client, SC450AI_REG_MIPI_CTRL, + SC450AI_REG_VALUE_08BIT, + SC450AI_MIPI_CTRL_OFF); + dev_info(&sc450ai->client->dev, + "quickstream, streaming off: enter soft standby mode\n"); + } + } break; + case RKCIS_CMD_SELECT_SETTING: + setting = (struct rk_sensor_setting *)arg; + ret = sc450ai_set_setting(sc450ai, setting); + break; + default: ret = -ENOIOCTLCMD; break; @@ -1827,6 +2119,7 @@ static long sc450ai_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_inf *inf; struct rkmodule_hdr_cfg *hdr; struct preisp_hdrae_exp_s *hdrae; + struct rk_sensor_setting *setting; long ret; u32 stream = 0; @@ -1894,6 +2187,21 @@ static long sc450ai_compat_ioctl32(struct v4l2_subdev *sd, else ret = -EFAULT; break; + case RKCIS_CMD_SELECT_SETTING: + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(setting, up, sizeof(*setting)); + if (!ret) + ret = sc450ai_ioctl(sd, cmd, setting); + else + ret = -EFAULT; + kfree(setting); + break; + default: ret = -ENOIOCTLCMD; break; @@ -1997,7 +2305,8 @@ static int sc450ai_s_power(struct v4l2_subdev *sd, int on) } if (!sc450ai->is_thunderboot) { - ret = sc450ai_write_array(sc450ai->client, sc450ai_global_regs); + ret = sc450ai_write_array(sc450ai->client, + sc450ai->cur_mode->global_reg_list); if (ret) { v4l2_err(sd, "could not set init registers\n"); pm_runtime_put_noidle(&client->dev); @@ -2020,7 +2329,7 @@ unlock_and_return: /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 sc450ai_cal_delay(u32 cycles, struct sc450ai *sc450ai) { - return DIV_ROUND_UP(cycles, sc450ai->cur_mode->xvclk_freq / 1000 / 1000); + return DIV_ROUND_UP(cycles, sc450ai->cur_mode->mclk / 1000 / 1000); } static int __sc450ai_power_on(struct sc450ai *sc450ai) @@ -2035,12 +2344,12 @@ static int __sc450ai_power_on(struct sc450ai *sc450ai) if (ret < 0) dev_err(dev, "could not set pins\n"); } - ret = clk_set_rate(sc450ai->xvclk, sc450ai->cur_mode->xvclk_freq); + ret = clk_set_rate(sc450ai->xvclk, sc450ai->cur_mode->mclk); if (ret < 0) - dev_warn(dev, "Failed to set xvclk rate (%dHz)\n", sc450ai->cur_mode->xvclk_freq); - if (clk_get_rate(sc450ai->xvclk) != sc450ai->cur_mode->xvclk_freq) + dev_warn(dev, "Failed to set xvclk rate (%dHz)\n", sc450ai->cur_mode->mclk); + if (clk_get_rate(sc450ai->xvclk) != sc450ai->cur_mode->mclk) dev_warn(dev, "xvclk mismatched, modes are based on %dHz\n", - sc450ai->cur_mode->xvclk_freq); + sc450ai->cur_mode->mclk); ret = clk_prepare_enable(sc450ai->xvclk); if (ret < 0) { dev_err(dev, "Failed to enable xvclk\n"); @@ -2122,6 +2431,11 @@ static int __maybe_unused sc450ai_resume(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct sc450ai *sc450ai = to_sc450ai(sd); + if (sc450ai->standby_hw) { + dev_info(dev, "resume standby!"); + return 0; + } + cam_sw_prepare_wakeup(sc450ai->cam_sw_inf, dev); usleep_range(4000, 5000); @@ -2132,7 +2446,7 @@ static int __maybe_unused sc450ai_resume(struct device *dev) if (sc450ai->has_init_exp && sc450ai->cur_mode != NO_HDR) { // hdr mode ret = sc450ai_ioctl(&sc450ai->subdev, PREISP_CMD_SET_HDRAE_EXP, - &sc450ai->cam_sw_inf->hdr_ae); + &sc450ai->cam_sw_inf->hdr_ae); if (ret) { dev_err(&sc450ai->client->dev, "set exp fail in hdr mode\n"); return ret; @@ -2148,6 +2462,11 @@ static int __maybe_unused sc450ai_suspend(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct sc450ai *sc450ai = to_sc450ai(sd); + if (sc450ai->standby_hw) { + dev_info(dev, "suspend standby!"); + return 0; + } + cam_sw_write_array_cb_init(sc450ai->cam_sw_inf, client, (void *)sc450ai->cur_mode->reg_list, (sensor_write_array)sc450ai_write_array); @@ -2223,7 +2542,9 @@ static int sc450ai_enum_frame_interval(struct v4l2_subdev *sd, static const struct dev_pm_ops sc450ai_pm_ops = { SET_RUNTIME_PM_OPS(sc450ai_runtime_suspend, sc450ai_runtime_resume, NULL) +#ifdef CONFIG_VIDEO_CAM_SLEEP_WAKEUP SET_LATE_SYSTEM_SLEEP_PM_OPS(sc450ai_suspend, sc450ai_resume) +#endif }; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API @@ -2243,6 +2564,7 @@ static const struct v4l2_subdev_core_ops sc450ai_core_ops = { static const struct v4l2_subdev_video_ops sc450ai_video_ops = { .s_stream = sc450ai_s_stream, .g_frame_interval = sc450ai_g_frame_interval, + .s_frame_interval = sc450ai_s_frame_interval, }; static const struct v4l2_subdev_pad_ops sc450ai_pad_ops = { @@ -2281,7 +2603,7 @@ static int sc450ai_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = sc450ai->cur_mode->height + ctrl->val - 5; + max = sc450ai->cur_mode->height + ctrl->val - 4; __v4l2_ctrl_modify_range(sc450ai->exposure, sc450ai->exposure->minimum, max, sc450ai->exposure->step, @@ -2289,6 +2611,11 @@ static int sc450ai_set_ctrl(struct v4l2_ctrl *ctrl) break; } + if (sc450ai->standby_hw && sc450ai->is_standby) { + dev_dbg(&client->dev, "%s: is_standby = true, will return\n", __func__); + return 0; + } + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; @@ -2296,7 +2623,7 @@ static int sc450ai_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE: dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val); if (sc450ai->cur_mode->hdr_mode == NO_HDR) { - val = ctrl->val<<1; + val = ctrl->val << 1; /* 4 least significant bits of expsoure are fractional part */ ret = sc450ai_write_reg(sc450ai->client, SC450AI_REG_EXPOSURE_H, @@ -2315,7 +2642,7 @@ static int sc450ai_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_ANALOGUE_GAIN: dev_dbg(&client->dev, "set gain 0x%x\n", ctrl->val); if (sc450ai->cur_mode->hdr_mode == NO_HDR) - ret = sc450ai_set_gain_reg(sc450ai, ctrl->val); + ret = sc450ai_set_gain_reg(sc450ai, ctrl->val, SC450AI_LGAIN); break; case V4L2_CID_VBLANK: dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val); @@ -2374,6 +2701,7 @@ static int sc450ai_initialize_controls(struct sc450ai *sc450ai) int ret; u64 dst_link_freq = 0; u64 dst_pixel_rate = 0, max_dst_pixel_rate = 0; + u8 lanes = sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes; handler = &sc450ai->ctrl_handler; mode = sc450ai->cur_mode; @@ -2389,9 +2717,9 @@ static int sc450ai_initialize_controls(struct sc450ai *sc450ai) sc450ai->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; dst_link_freq = mode->link_freq_idx; - max_dst_pixel_rate = SC450AI_MAX_LINK_FREQ / SC450AI_BITS_PER_SAMPLE * 2 * SC450AI_LANES_4LANE; + max_dst_pixel_rate = SC450AI_MAX_LINK_FREQ / mode->bpp * 2 * lanes; dst_pixel_rate = (u32)link_freq_menu_items[mode->link_freq_idx] / - SC450AI_BITS_PER_SAMPLE * 2 * mode->lanes; + mode->bpp * 2 * lanes; sc450ai->pixel_rate = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, max_dst_pixel_rate, 1, dst_pixel_rate); @@ -2408,7 +2736,7 @@ static int sc450ai_initialize_controls(struct sc450ai *sc450ai) V4L2_CID_VBLANK, vblank_def, SC450AI_VTS_MAX - mode->height, 1, vblank_def); - exposure_max = mode->vts_def - 8; + exposure_max = mode->vts_def - 4; sc450ai->exposure = v4l2_ctrl_new_std(handler, &sc450ai_ctrl_ops, V4L2_CID_EXPOSURE, SC450AI_EXPOSURE_MIN, exposure_max, SC450AI_EXPOSURE_STEP, @@ -2436,6 +2764,7 @@ static int sc450ai_initialize_controls(struct sc450ai *sc450ai) sc450ai->subdev.ctrl_handler = handler; sc450ai->has_init_exp = false; sc450ai->cur_fps = mode->max_fps; + sc450ai->is_standby = false; return 0; @@ -2464,7 +2793,7 @@ static int sc450ai_check_sensor_id(struct sc450ai *sc450ai, return -ENODEV; } - dev_info(dev, "Detected %s sensor chip_id %x\n", SC450AI_NAME, CHIP_ID); + dev_info(dev, "Detected SC450AI (0x%04x) sensor\n", CHIP_ID); return 0; } @@ -2515,10 +2844,15 @@ static int sc450ai_probe(struct i2c_client *client, return -EINVAL; } - sc450ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + /* Compatible with non-standby mode if this attribute is not configured in dts*/ + of_property_read_u32(node, RKMODULE_CAMERA_STANDBY_HW, + &sc450ai->standby_hw); + dev_info(dev, "sc450ai->standby_hw = %d\n", sc450ai->standby_hw); - ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, - &hdr_mode); + sc450ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + dev_err(dev, "========= is_thunderboot %d\n", sc450ai->is_thunderboot); + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); if (ret) { hdr_mode = NO_HDR; dev_warn(dev, " Get hdr mode failed! no hdr default\n"); @@ -2538,12 +2872,16 @@ static int sc450ai_probe(struct i2c_client *client, return -EINVAL; } - if (sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes == SC450AI_LANES_4LANE) { + if (sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes == 4) { sc450ai->supported_modes = supported_modes_4lane; sc450ai->cfg_num = ARRAY_SIZE(supported_modes_4lane); + dev_info(dev, "detect sc450ai lane: %d\n", + sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes); } else { sc450ai->supported_modes = supported_modes_2lane; sc450ai->cfg_num = ARRAY_SIZE(supported_modes_2lane); + dev_info(dev, "detect sc450ai lane: %d\n", + sc450ai->bus_cfg.bus.mipi_csi2.num_data_lanes); } sc450ai->client = client; @@ -2636,7 +2974,7 @@ static int sc450ai_probe(struct i2c_client *client, if (!sc450ai->cam_sw_inf) { sc450ai->cam_sw_inf = cam_sw_init(); cam_sw_clk_init(sc450ai->cam_sw_inf, sc450ai->xvclk, - sc450ai->cur_mode->xvclk_freq); + sc450ai->cur_mode->mclk); cam_sw_reset_pin_init(sc450ai->cam_sw_inf, sc450ai->reset_gpio, 0); cam_sw_pwdn_pin_init(sc450ai->cam_sw_inf, sc450ai->pwdn_gpio, 1); }