@@ -149,6 +149,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 */
@@ -337,6 +343,10 @@ struct imx219 {
/* Two or Four lanes */
u8 lanes;
+
+ /* Binning mode */
+ enum binning_mode bin_h;
+ enum binning_mode bin_v;
};
static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
@@ -362,6 +372,36 @@ 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 int imx219_get_rate_factor(struct imx219 *imx219)
+{
+ switch (imx219->bin_v) {
+ case BINNING_NONE:
+ case BINNING_X2:
+ return 1;
+ case BINNING_ANALOG_X2:
+ return 2;
+ }
+ return -EINVAL;
+}
+
/* -----------------------------------------------------------------------------
* Controls
*/
@@ -373,10 +413,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;
+ int 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;
@@ -405,7 +447,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,
@@ -422,7 +464,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_HBLANK:
cci_write(imx219->regmap, IMX219_REG_HTS,
@@ -463,7 +505,8 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
static unsigned long imx219_get_pixel_rate(struct imx219 *imx219)
{
- return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE;
+ return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE :
+ IMX219_PIXEL_RATE_4LANE) * imx219_get_rate_factor(imx219);
}
/* Initialize control handlers */
@@ -592,29 +635,12 @@ 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;
+ 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);
@@ -625,28 +651,8 @@ 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);
+ cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, imx219->bin_h, &ret);
+ cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, imx219->bin_v, &ret);
cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE,
format->width, &ret);
@@ -851,6 +857,27 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
int exposure_max;
int exposure_def;
int hblank;
+ int pixel_rate;
+ u32 bpp = imx219_get_format_bpp(format);
+ enum binning_mode binning = BINNING_NONE;
+
+ /*
+ * For 8-bit formats, analog horizontal binning is required,
+ * else the output image is garbage.
+ * For 10-bit formats, analog horizontal binning is optional,
+ * but still useful as it doubles the effective framerate.
+ * We can only use it with width <= 1624, as for higher values
+ * there are blocky artefacts.
+ *
+ * Vertical binning should match the horizontal binning mode.
+ */
+ if (bin_h == 2 && (format->width <= 1624 || bpp == 8))
+ binning = BINNING_ANALOG_X2;
+ else
+ binning = BINNING_X2;
+
+ imx219->bin_h = (bin_h == 2) ? binning : BINNING_NONE;
+ imx219->bin_v = (bin_v == 2) ? binning : BINNING_NONE;
/* Update limits and set FPS to default */
__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
@@ -879,6 +906,11 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
IMX219_PPL_MAX - mode->width,
1, IMX219_PPL_MIN - mode->width);
__v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+
+ /* Scale the pixel rate based on the mode specific factor */
+ pixel_rate = imx219_get_pixel_rate(imx219);
+ __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
+ pixel_rate, 1, pixel_rate);
}
return 0;