Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / media / platform / vsp1 / vsp1_rpf.c
index 254871d..d14d26b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * vsp1_rpf.c  --  R-Car VSP1 Read Pixel Formatter
  *
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -39,6 +39,36 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
 }
 
 /* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static int rpf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vsp1_rwpf *rpf =
+               container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
+       struct vsp1_pipeline *pipe;
+
+       if (!vsp1_entity_is_streaming(&rpf->entity))
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_ALPHA_COMPONENT:
+               vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
+                              ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+
+               pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity);
+               vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val);
+               break;
+       }
+
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops rpf_ctrl_ops = {
+       .s_ctrl = rpf_s_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
  * V4L2 Subdevice Core Operations
  */
 
@@ -47,25 +77,41 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
        struct vsp1_rwpf *rpf = to_rwpf(subdev);
        const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
        const struct v4l2_pix_format_mplane *format = &rpf->video.format;
+       const struct v4l2_rect *crop = &rpf->crop;
        u32 pstride;
        u32 infmt;
+       int ret;
+
+       ret = vsp1_entity_set_streaming(&rpf->entity, enable);
+       if (ret < 0)
+               return ret;
 
        if (!enable)
                return 0;
 
-       /* Source size and stride. Cropping isn't supported yet. */
+       /* Source size, stride and crop offsets.
+        *
+        * The crop offsets correspond to the location of the crop rectangle top
+        * left corner in the plane buffer. Only two offsets are needed, as
+        * planes 2 and 3 always have identical strides.
+        */
        vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
-                      (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
-                      (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+                      (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+                      (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
        vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
-                      (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
-                      (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+                      (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+                      (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
 
+       rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
+                       + crop->left * fmtinfo->bpp[0] / 8;
        pstride = format->plane_fmt[0].bytesperline
                << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
-       if (format->num_planes > 1)
+       if (format->num_planes > 1) {
+               rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
+                               + crop->left * fmtinfo->bpp[1] / 8;
                pstride |= format->plane_fmt[1].bytesperline
                        << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
+       }
 
        vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
 
@@ -85,15 +131,18 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
        vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
        vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
 
-       /* Output location. Composing isn't supported yet. */
-       vsp1_rpf_write(rpf, VI6_RPF_LOC, 0);
+       /* Output location */
+       vsp1_rpf_write(rpf, VI6_RPF_LOC,
+                      (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
+                      (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
 
-       /* Disable alpha, mask and color key. Set the alpha channel to a fixed
-        * value of 255.
+       /* Use the alpha channel (extended to 8 bits) when available or an
+        * alpha value set through the V4L2_CID_ALPHA_COMPONENT control
+        * otherwise. Disable color keying.
         */
-       vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
-       vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
-                      255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+       vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
+                      (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
+                                      : VI6_RPF_ALPH_SEL_ASEL_FIXED));
        vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
        vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
 
@@ -113,6 +162,8 @@ static struct v4l2_subdev_pad_ops rpf_pad_ops = {
        .enum_frame_size = vsp1_rwpf_enum_frame_size,
        .get_fmt = vsp1_rwpf_get_format,
        .set_fmt = vsp1_rwpf_set_format,
+       .get_selection = vsp1_rwpf_get_selection,
+       .set_selection = vsp1_rwpf_set_selection,
 };
 
 static struct v4l2_subdev_ops rpf_ops = {
@@ -129,11 +180,14 @@ static void rpf_vdev_queue(struct vsp1_video *video,
 {
        struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
 
-       vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]);
+       vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
+                      buf->addr[0] + rpf->offsets[0]);
        if (buf->buf.num_planes > 1)
-               vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]);
+               vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
+                              buf->addr[1] + rpf->offsets[1]);
        if (buf->buf.num_planes > 2)
-               vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]);
+               vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
+                              buf->addr[2] + rpf->offsets[1]);
 }
 
 static const struct vsp1_video_operations rpf_vdev_ops = {
@@ -160,7 +214,6 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
 
        rpf->entity.type = VSP1_ENTITY_RPF;
        rpf->entity.index = index;
-       rpf->entity.id = VI6_DPR_NODE_RPF(index);
 
        ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
        if (ret < 0)
@@ -179,6 +232,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
 
        vsp1_entity_init_formats(subdev, NULL);
 
+       /* Initialize the control handler. */
+       v4l2_ctrl_handler_init(&rpf->ctrls, 1);
+       v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
+                         0, 255, 1, 255);
+
+       rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
+
+       if (rpf->ctrls.error) {
+               dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
+                       index);
+               ret = rpf->ctrls.error;
+               goto error;
+       }
+
        /* Initialize the video device. */
        video = &rpf->video;
 
@@ -188,7 +255,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
 
        ret = vsp1_video_init(video, &rpf->entity);
        if (ret < 0)
-               goto error_video;
+               goto error;
+
+       rpf->entity.video = video;
 
        /* Connect the video device to the RPF. */
        ret = media_entity_create_link(&rpf->video.video.entity, 0,
@@ -197,13 +266,11 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
                                       MEDIA_LNK_FL_ENABLED |
                                       MEDIA_LNK_FL_IMMUTABLE);
        if (ret < 0)
-               goto error_link;
+               goto error;
 
        return rpf;
 
-error_link:
-       vsp1_video_cleanup(video);
-error_video:
-       media_entity_cleanup(&rpf->entity.subdev.entity);
+error:
+       vsp1_entity_destroy(&rpf->entity);
        return ERR_PTR(ret);
 }