diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 485e2fff90a7b2..83de9d59b5c5c1 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -148,6 +148,12 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U +enum binning_mode { + BINNING_NONE = IMX219_BINNING_NONE, + BINNING_X2 = IMX219_BINNING_X2, + BINNING_ANALOG_X2 = IMX219_BINNING_X2_ANALOG, +}; + /* Mode : resolution and related config&values */ struct imx219_mode { /* Frame width */ @@ -324,13 +330,13 @@ static const struct imx219_mode supported_modes[] = { .vts_def = 1763, }, { - /* 2x2 binned 30fps mode */ + /* 2x2 binned 60fps mode */ .width = 1640, .height = 1232, .vts_def = 1763, }, { - /* 640x480 30fps mode */ + /* 640x480 60fps mode */ .width = 640, .height = 480, .vts_def = 1763, @@ -385,6 +391,59 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } +static u32 imx219_get_format_bpp(const struct v4l2_mbus_framefmt *format) +{ + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return 8; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static enum binning_mode imx219_get_binning(struct imx219 *imx219, u8 *bin_h, + u8 *bin_v) +{ + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&imx219->sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); + + *bin_h = crop->width / format->width; + *bin_v = crop->height / format->height; + + if (*bin_h == 2 && *bin_v == 2) + return BINNING_ANALOG_X2; + else if (*bin_h == 2 || *bin_v == 2) + /* + * Don't use analog binning if only one dimension + * is binned, as it crops the other dimension + */ + return BINNING_X2; + else + return BINNING_NONE; +} + +static inline u32 imx219_get_rate_factor(struct imx219 *imx219) +{ + u8 bin_h, bin_v; + enum binning_mode binning = imx219_get_binning(imx219, &bin_h, &bin_v); + + if (binning == BINNING_ANALOG_X2) + return 2; + + return 1; +} + /* ----------------------------------------------------------------------------- * Controls */ @@ -396,10 +455,12 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + u32 rate_factor; int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); format = v4l2_subdev_state_get_format(state, 0); + rate_factor = imx219_get_rate_factor(imx219); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -428,7 +489,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val, &ret); + ctrl->val / rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -445,7 +506,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - format->height + ctrl->val, &ret); + (format->height + ctrl->val) / rate_factor, &ret); break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, @@ -613,29 +674,14 @@ static int imx219_set_framefmt(struct imx219 *imx219, { const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; - unsigned int bpp; - u64 bin_h, bin_v; + enum binning_mode binning; + u8 bin_h, bin_v; + u32 bpp; int ret = 0; format = v4l2_subdev_state_get_format(state, 0); crop = v4l2_subdev_state_get_crop(state, 0); - - switch (format->code) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - bpp = 8; - break; - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - default: - bpp = 10; - break; - } + bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); @@ -646,28 +692,11 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - switch (crop->width / format->width) { - case 1: - default: - bin_h = IMX219_BINNING_NONE; - break; - case 2: - bin_h = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - - switch (crop->height / format->height) { - case 1: - default: - bin_v = IMX219_BINNING_NONE; - break; - case 2: - bin_v = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - - cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); - cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, bin_v, &ret); + binning = imx219_get_binning(imx219, &bin_h, &bin_v); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, + (bin_h == 2) ? binning : BINNING_NONE, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, + (bin_v == 2) ? binning : BINNING_NONE, &ret); cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, format->width, &ret); @@ -873,6 +902,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, int exposure_max; int exposure_def; int hblank; + int pixel_rate; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, @@ -896,6 +926,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, hblank = IMX219_PPL_DEFAULT - mode->width; __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank); + + /* Scale the pixel rate based on the mode specific factor */ + pixel_rate = imx219_get_pixel_rate(imx219) * + imx219_get_rate_factor(imx219); + __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); } return 0;