Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 81 additions & 45 deletions drivers/media/i2c/imx219.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down