diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e6188900f5ea..6fdcaa580ead 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -81,6 +81,7 @@ struct csi_priv { const struct imx_media_pixfmt *cc[CSI_NUM_PADS]; struct v4l2_fract frame_interval[CSI_NUM_PADS]; struct v4l2_rect crop; + struct v4l2_rect compose; const struct csi_skip_desc *skip; /* active vb2 buffers to send to video dev sink */ @@ -589,8 +590,8 @@ static int csi_setup(struct csi_priv *priv) ipu_csi_set_window(priv->csi, &priv->crop); ipu_csi_set_downsize(priv->csi, - priv->crop.width == 2 * outfmt->width, - priv->crop.height == 2 * outfmt->height); + priv->crop.width == 2 * priv->compose.width, + priv->crop.height == 2 * priv->compose.height); ipu_csi_init_interface(priv->csi, &sensor_mbus_cfg, &if_fmt); @@ -1048,6 +1049,17 @@ __csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, return &priv->crop; } +static struct v4l2_rect * +__csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&priv->sd, cfg, + CSI_SINK_PAD); + else + return &priv->compose; +} + static void csi_try_crop(struct csi_priv *priv, struct v4l2_rect *crop, struct v4l2_subdev_pad_config *cfg, @@ -1157,6 +1169,7 @@ static void csi_try_fmt(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *sdformat, struct v4l2_rect *crop, + struct v4l2_rect *compose, const struct imx_media_pixfmt **cc) { const struct imx_media_pixfmt *incc; @@ -1171,15 +1184,8 @@ static void csi_try_fmt(struct csi_priv *priv, incc = imx_media_find_mbus_format(infmt->code, CS_SEL_ANY, true); - if (sdformat->format.width < crop->width * 3 / 4) - sdformat->format.width = crop->width / 2; - else - sdformat->format.width = crop->width; - - if (sdformat->format.height < crop->height * 3 / 4) - sdformat->format.height = crop->height / 2; - else - sdformat->format.height = crop->height; + sdformat->format.width = compose->width; + sdformat->format.height = compose->height; if (incc->bayer) { sdformat->format.code = infmt->code; @@ -1215,11 +1221,17 @@ static void csi_try_fmt(struct csi_priv *priv, v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); + + /* Reset crop and compose rectangles */ crop->left = 0; crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; csi_try_crop(priv, crop, cfg, &sdformat->format, sensor); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); @@ -1244,7 +1256,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct imx_media_subdev *sensor; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; - struct v4l2_rect *crop; + struct v4l2_rect *crop, *compose; int ret = 0; if (sdformat->pad >= CSI_NUM_PADS) @@ -1264,8 +1276,9 @@ static int csi_set_fmt(struct v4l2_subdev *sd, } crop = __csi_get_crop(priv, cfg, sdformat->which); + compose = __csi_get_compose(priv, cfg, sdformat->which); - csi_try_fmt(priv, sensor, cfg, sdformat, crop, &cc); + csi_try_fmt(priv, sensor, cfg, sdformat, crop, compose, &cc); fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which); *fmt = sdformat->format; @@ -1282,7 +1295,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd, format.pad = pad; format.which = sdformat->which; format.format = sdformat->format; - csi_try_fmt(priv, sensor, cfg, &format, crop, &outcc); + csi_try_fmt(priv, sensor, cfg, &format, NULL, compose, + &outcc); outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which); *outfmt = format.format; @@ -1316,16 +1330,17 @@ static int csi_get_selection(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *infmt; - struct v4l2_rect *crop; + struct v4l2_rect *crop, *compose; int ret = 0; - if (sel->pad >= CSI_NUM_PADS || sel->pad == CSI_SINK_PAD) + if (sel->pad != CSI_SINK_PAD) return -EINVAL; mutex_lock(&priv->lock); infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which); crop = __csi_get_crop(priv, cfg, sel->which); + compose = __csi_get_compose(priv, cfg, sel->which); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -1337,6 +1352,15 @@ static int csi_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP: sel->r = *crop; break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = crop->width; + sel->r.height = crop->height; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *compose; + break; default: ret = -EINVAL; } @@ -1345,19 +1369,34 @@ static int csi_get_selection(struct v4l2_subdev *sd, return ret; } +static int csi_set_scale(u32 *compose, u32 crop, u32 flags) +{ + if ((flags & (V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE)) == + (V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE) && + *compose != crop && *compose != crop / 2) + return -ERANGE; + + if (*compose <= crop / 2 || + (*compose < crop * 3 / 4 && !(flags & V4L2_SEL_FLAG_GE)) || + (*compose < crop && (flags & V4L2_SEL_FLAG_LE))) + *compose = crop / 2; + else + *compose = crop; + + return 0; +} + static int csi_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *infmt; + struct v4l2_rect *crop, *compose; struct imx_media_subdev *sensor; - struct v4l2_rect *crop; int pad, ret = 0; - if (sel->pad >= CSI_NUM_PADS || - sel->pad == CSI_SINK_PAD || - sel->target != V4L2_SEL_TGT_CROP) + if (sel->pad != CSI_SINK_PAD) return -EINVAL; sensor = imx_media_find_sensor(priv->md, &priv->sd.entity); @@ -1375,30 +1414,66 @@ static int csi_set_selection(struct v4l2_subdev *sd, infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which); crop = __csi_get_crop(priv, cfg, sel->which); + compose = __csi_get_compose(priv, cfg, sel->which); - /* - * Modifying the crop rectangle always changes the format on the source - * pad. If the KEEP_CONFIG flag is set, just return the current crop - * rectangle. - */ - if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = priv->crop; - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - *crop = sel->r; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + /* + * Modifying the crop rectangle always changes the format on + * the source pads. If the KEEP_CONFIG flag is set, just return + * the current crop rectangle. + */ + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { + sel->r = priv->crop; + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + *crop = sel->r; + goto out; + } + + csi_try_crop(priv, &sel->r, cfg, infmt, sensor); + + *crop = sel->r; + + /* Reset scaling to 1:1 */ + compose->width = crop->width; + compose->height = crop->height; + break; + case V4L2_SEL_TGT_COMPOSE: + /* + * Modifying the compose rectangle always changes the format on + * the source pads. If the KEEP_CONFIG flag is set, just return + * the current compose rectangle. + */ + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { + sel->r = priv->compose; + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + *compose = sel->r; + goto out; + } + + sel->r.left = 0; + sel->r.top = 0; + ret = csi_set_scale(&sel->r.width, crop->width, sel->flags); + if (ret) + goto out; + ret = csi_set_scale(&sel->r.height, crop->height, sel->flags); + if (ret) + goto out; + + *compose = sel->r; + break; + default: + ret = -EINVAL; goto out; } - csi_try_crop(priv, &sel->r, cfg, infmt, sensor); - - *crop = sel->r; - - /* Update the source pad formats */ + /* Reset source pads to sink compose rectangle */ for (pad = CSI_SINK_PAD + 1; pad < CSI_NUM_PADS; pad++) { struct v4l2_mbus_framefmt *outfmt; outfmt = __csi_get_fmt(priv, cfg, pad, sel->which); - outfmt->width = crop->width; - outfmt->height = crop->height; + outfmt->width = compose->width; + outfmt->height = compose->height; } out: @@ -1465,6 +1540,12 @@ static int csi_registered(struct v4l2_subdev *sd) /* disable frame skipping */ priv->skip = &csi_skip[0]; + /* init default crop and compose rectangle sizes */ + priv->crop.width = 640; + priv->crop.height = 480; + priv->compose.width = 640; + priv->compose.height = 480; + priv->fim = imx_media_fim_init(&priv->sd); if (IS_ERR(priv->fim)) { ret = PTR_ERR(priv->fim);