提交 942aa1ee 编写于 作者: N Naushir Patuck 提交者: Zheng Zengkai

media: i2c: imx477: Add support for adaptive frame control

raspberrypi inclusion
category: feature
bugzilla: 50432

--------------------------------

Use V4L2_CID_EXPOSURE_AUTO_PRIORITY to control if the driver should
automatically adjust the sensor frame length based on exposure time,
allowing variable frame rates and longer exposures.
Signed-off-by: NNaushir Patuck <naush@raspberrypi.com>
Signed-off-by: NFang Yafen <yafen@iscas.ac.cn>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 fa6d51b6
...@@ -1082,6 +1082,8 @@ struct imx477 { ...@@ -1082,6 +1082,8 @@ struct imx477 {
struct v4l2_ctrl *hflip; struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vblank; struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank; struct v4l2_ctrl *hblank;
/* This ctrl allows automatic variable framerate */
struct v4l2_ctrl *exposure_auto;
/* Current mode */ /* Current mode */
const struct imx477_mode *mode; const struct imx477_mode *mode;
...@@ -1278,24 +1280,86 @@ static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ...@@ -1278,24 +1280,86 @@ static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return 0; return 0;
} }
static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) static int imx477_set_exposure(struct imx477 *imx477, unsigned int val)
{ {
struct imx477 *imx477 = int ret;
container_of(ctrl->handler, struct imx477, ctrl_handler);
struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
int ret = 0;
if (ctrl->id == V4L2_CID_VBLANK) { ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
IMX477_REG_VALUE_16BIT, val);
/* Setup the frame length in the case of auto framerate mode. */
if (imx477->exposure_auto->val) {
unsigned int frame_length, frame_length_max, frame_length_min;
frame_length_min = imx477->vblank->minimum +
imx477->mode->height;
frame_length_max = imx477->vblank->maximum +
imx477->mode->height;
frame_length = max(frame_length_min,
val + IMX477_EXPOSURE_OFFSET);
frame_length = min(frame_length_max, frame_length);
ret += imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
IMX477_REG_VALUE_16BIT, frame_length);
}
return ret;
}
static void imx477_adjust_exposure_range(struct imx477 *imx477,
struct v4l2_ctrl *ctrl)
{
int exposure_max, exposure_def; int exposure_max, exposure_def;
/* Update max exposure while meeting expected vblanking */ if (ctrl->id == V4L2_CID_VBLANK || !ctrl->val) {
exposure_max = imx477->mode->height + ctrl->val - /*
* Either VBLANK has been changed or auto framerate
* adjusting has been disabled. Honour the VBLANK limits
* when setting exposure.
*/
exposure_max = imx477->mode->height + imx477->vblank->val -
IMX477_EXPOSURE_OFFSET; IMX477_EXPOSURE_OFFSET;
if (ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) {
/*
* Allow VBLANK adjustments since the driver is not
* handling frame length control automatically.
*/
__v4l2_ctrl_grab(imx477->vblank, false);
}
} else {
/*
* Auto framerate adjusting has been enabled. VBLANK
* ctrl has been disabled and exposure can ramp up
* to the maximum allowable value.
*/
exposure_max = IMX477_EXPOSURE_MAX;
/*
* Do not allow VBLANK adjustments if the driver is
* handling it frame length control automatically.
*/
__v4l2_ctrl_grab(imx477->vblank, true);
}
exposure_def = min(exposure_max, imx477->exposure->val); exposure_def = min(exposure_max, imx477->exposure->val);
__v4l2_ctrl_modify_range(imx477->exposure, __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
imx477->exposure->minimum,
exposure_max, imx477->exposure->step, exposure_max, imx477->exposure->step,
exposure_def); exposure_def);
}
static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx477 *imx477 =
container_of(ctrl->handler, struct imx477, ctrl_handler);
struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
int ret = 0;
if (ctrl->id == V4L2_CID_VBLANK ||
ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) {
/*
* These controls may change the limits of usable exposure,
* so check and adjust if necessary.
*/
imx477_adjust_exposure_range(imx477, ctrl);
} }
/* /*
...@@ -1311,8 +1375,14 @@ static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) ...@@ -1311,8 +1375,14 @@ static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
IMX477_REG_VALUE_16BIT, ctrl->val); IMX477_REG_VALUE_16BIT, ctrl->val);
break; break;
case V4L2_CID_EXPOSURE: case V4L2_CID_EXPOSURE:
ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE, ret = imx477_set_exposure(imx477, ctrl->val);
IMX477_REG_VALUE_16BIT, ctrl->val); break;
case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
/*
* imx477_set_exposure() will recalculate the frame length
* to adjust the framerate to match the exposure.
*/
ret = imx477_set_exposure(imx477, imx477->exposure->val);
break; break;
case V4L2_CID_DIGITAL_GAIN: case V4L2_CID_DIGITAL_GAIN:
ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN, ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
...@@ -1510,9 +1580,8 @@ unsigned int imx477_get_frame_length(const struct imx477_mode *mode, ...@@ -1510,9 +1580,8 @@ unsigned int imx477_get_frame_length(const struct imx477_mode *mode,
static void imx477_set_framing_limits(struct imx477 *imx477) static void imx477_set_framing_limits(struct imx477 *imx477)
{ {
unsigned int frm_length_min, frm_length_default, hblank;
const struct imx477_mode *mode = imx477->mode; const struct imx477_mode *mode = imx477->mode;
unsigned int frm_length_min, frm_length_default;
unsigned int exposure_max, exposure_def, hblank;
frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min); frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min);
frm_length_default = frm_length_default =
...@@ -1522,15 +1591,10 @@ static void imx477_set_framing_limits(struct imx477 *imx477) ...@@ -1522,15 +1591,10 @@ static void imx477_set_framing_limits(struct imx477 *imx477)
__v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height, __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
IMX477_FRAME_LENGTH_MAX - mode->height, IMX477_FRAME_LENGTH_MAX - mode->height,
1, frm_length_default - mode->height); 1, frm_length_default - mode->height);
/* Setting this will adjust the exposure limits as well. */
__v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height);
/* Update max exposure while meeting expected vblanking */
exposure_max = IMX477_FRAME_LENGTH_MAX - IMX477_EXPOSURE_OFFSET;
exposure_def = frm_length_default - mode->height -
IMX477_EXPOSURE_OFFSET;
__v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
exposure_max, imx477->exposure->step,
exposure_def);
/* /*
* Currently PPL is fixed to the mode specified value, so hblank * Currently PPL is fixed to the mode specified value, so hblank
* depends on mode->width only, and is not changeable in any * depends on mode->width only, and is not changeable in any
...@@ -1939,6 +2003,11 @@ static int imx477_init_controls(struct imx477 *imx477) ...@@ -1939,6 +2003,11 @@ static int imx477_init_controls(struct imx477 *imx477)
IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX, IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX,
IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT); IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT);
imx477->exposure_auto =
v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
V4L2_CID_EXPOSURE_AUTO_PRIORITY,
0, 1, 1, 0);
imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0); V4L2_CID_HFLIP, 0, 1, 1, 0);
if (imx477->hflip) if (imx477->hflip)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册