summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov6650.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov6650.c')
-rw-r--r--drivers/media/i2c/ov6650.c115
1 files changed, 70 insertions, 45 deletions
diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c
index f67412150b16..eb59dc8bb592 100644
--- a/drivers/media/i2c/ov6650.c
+++ b/drivers/media/i2c/ov6650.c
@@ -472,9 +472,16 @@ static int ov6650_get_selection(struct v4l2_subdev *sd,
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_rect *rect;
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ /* pre-select try crop rectangle */
+ rect = &sd_state->pads->try_crop;
+
+ } else {
+ /* pre-select active crop rectangle */
+ rect = &priv->rect;
+ }
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
@@ -483,14 +490,33 @@ static int ov6650_get_selection(struct v4l2_subdev *sd,
sel->r.width = W_CIF;
sel->r.height = H_CIF;
return 0;
+
case V4L2_SEL_TGT_CROP:
- sel->r = priv->rect;
+ /* use selected crop rectangle */
+ sel->r = *rect;
return 0;
+
default:
return -EINVAL;
}
}
+static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
+{
+ return width > rect->width >> 1 || height > rect->height >> 1;
+}
+
+static void ov6650_bind_align_crop_rectangle(struct v4l2_rect *rect)
+{
+ v4l_bound_align_image(&rect->width, 2, W_CIF, 1,
+ &rect->height, 2, H_CIF, 1, 0);
+ v4l_bound_align_image(&rect->left, DEF_HSTRT << 1,
+ (DEF_HSTRT << 1) + W_CIF - (__s32)rect->width, 1,
+ &rect->top, DEF_VSTRT << 1,
+ (DEF_VSTRT << 1) + H_CIF - (__s32)rect->height,
+ 1, 0);
+}
+
static int ov6650_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
@@ -499,18 +525,30 @@ static int ov6650_set_selection(struct v4l2_subdev *sd,
struct ov6650 *priv = to_ov6650(client);
int ret;
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
- sel->target != V4L2_SEL_TGT_CROP)
+ if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- v4l_bound_align_image(&sel->r.width, 2, W_CIF, 1,
- &sel->r.height, 2, H_CIF, 1, 0);
- v4l_bound_align_image(&sel->r.left, DEF_HSTRT << 1,
- (DEF_HSTRT << 1) + W_CIF - (__s32)sel->r.width, 1,
- &sel->r.top, DEF_VSTRT << 1,
- (DEF_VSTRT << 1) + H_CIF - (__s32)sel->r.height,
- 1, 0);
+ ov6650_bind_align_crop_rectangle(&sel->r);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *crop = &sd_state->pads->try_crop;
+ struct v4l2_mbus_framefmt *mf = &sd_state->pads->try_fmt;
+ /* detect current pad config scaling factor */
+ bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop);
+
+ /* store new crop rectangle */
+ *crop = sel->r;
+ /* adjust frame size */
+ mf->width = crop->width >> half_scale;
+ mf->height = crop->height >> half_scale;
+
+ return 0;
+ }
+
+ /* V4L2_SUBDEV_FORMAT_ACTIVE */
+
+ /* apply new crop rectangle */
ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1);
if (!ret) {
priv->rect.width += priv->rect.left - sel->r.left;
@@ -562,30 +600,13 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd,
return 0;
}
-static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
-{
- return width > rect->width >> 1 || height > rect->height >> 1;
-}
-
#define to_clkrc(div) ((div) - 1)
/* set the format we will capture in */
-static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+static int ov6650_s_fmt(struct v4l2_subdev *sd, u32 code, bool half_scale)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
- bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
- struct v4l2_subdev_selection sel = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .target = V4L2_SEL_TGT_CROP,
- .r.left = priv->rect.left + (priv->rect.width >> 1) -
- (mf->width >> (1 - half_scale)),
- .r.top = priv->rect.top + (priv->rect.height >> 1) -
- (mf->height >> (1 - half_scale)),
- .r.width = mf->width << half_scale,
- .r.height = mf->height << half_scale,
- };
- u32 code = mf->code;
u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask;
int ret;
@@ -653,9 +674,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
coma_mask |= COMA_QCIF;
}
- ret = ov6650_set_selection(sd, NULL, &sel);
- if (!ret)
- ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
+ ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
if (!ret) {
priv->half_scale = half_scale;
@@ -674,14 +693,12 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf = &format->format;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_rect *crop;
+ bool half_scale;
if (format->pad)
return -EINVAL;
- if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
- v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
- &mf->height, 2, H_CIF, 1, 0);
-
switch (mf->code) {
case MEDIA_BUS_FMT_Y10_1X10:
mf->code = MEDIA_BUS_FMT_Y8_1X8;
@@ -699,10 +716,17 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd,
break;
}
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ crop = &sd_state->pads->try_crop;
+ else
+ crop = &priv->rect;
+
+ half_scale = !is_unscaled_ok(mf->width, mf->height, crop);
+
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- /* store media bus format code and frame size in pad config */
- sd_state->pads->try_fmt.width = mf->width;
- sd_state->pads->try_fmt.height = mf->height;
+ /* store new mbus frame format code and size in pad config */
+ sd_state->pads->try_fmt.width = crop->width >> half_scale;
+ sd_state->pads->try_fmt.height = crop->height >> half_scale;
sd_state->pads->try_fmt.code = mf->code;
/* return default mbus frame format updated with pad config */
@@ -712,9 +736,11 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd,
mf->code = sd_state->pads->try_fmt.code;
} else {
- /* apply new media bus format code and frame size */
- int ret = ov6650_s_fmt(sd, mf);
+ int ret = 0;
+ /* apply new media bus frame format and scaling if changed */
+ if (mf->code != priv->code || half_scale != priv->half_scale)
+ ret = ov6650_s_fmt(sd, mf->code, half_scale);
if (ret)
return ret;
@@ -890,9 +916,8 @@ static int ov6650_video_probe(struct v4l2_subdev *sd)
if (!ret)
ret = ov6650_prog_dflt(client, xclk->clkrc);
if (!ret) {
- struct v4l2_mbus_framefmt mf = ov6650_def_fmt;
-
- ret = ov6650_s_fmt(sd, &mf);
+ /* driver default frame format, no scaling */
+ ret = ov6650_s_fmt(sd, ov6650_def_fmt.code, false);
}
if (!ret)
ret = v4l2_ctrl_handler_setup(&priv->hdl);