[media] ov9640: convert to the control framework
[pandora-kernel.git] / drivers / media / video / sh_mobile_ceu_camera.c
index e540898..5d5781b 100644 (file)
@@ -121,7 +121,7 @@ struct sh_mobile_ceu_dev {
 };
 
 struct sh_mobile_ceu_cam {
-       /* CEU offsets within scaled by the CEU camera output */
+       /* CEU offsets within the camera output, before the CEU scaler */
        unsigned int ceu_left;
        unsigned int ceu_top;
        /* Client output, as seen by the CEU */
@@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
        return container_of(vb, struct sh_mobile_ceu_buffer, vb);
 }
 
-static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
-{
-       unsigned long flags;
-
-       flags = SOCAM_MASTER |
-               SOCAM_PCLK_SAMPLE_RISING |
-               SOCAM_HSYNC_ACTIVE_HIGH |
-               SOCAM_HSYNC_ACTIVE_LOW |
-               SOCAM_VSYNC_ACTIVE_HIGH |
-               SOCAM_VSYNC_ACTIVE_LOW |
-               SOCAM_DATA_ACTIVE_HIGH;
-
-       if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
-               flags |= SOCAM_DATAWIDTH_8;
-
-       if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
-               flags |= SOCAM_DATAWIDTH_16;
-
-       if (flags & SOCAM_DATAWIDTH_MASK)
-               return flags;
-
-       return 0;
-}
-
 static void ceu_write(struct sh_mobile_ceu_dev *priv,
                      unsigned long reg_offs, u32 data)
 {
@@ -217,8 +193,9 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
  *  Videobuf operations
  */
 static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+                       const struct v4l2_format *fmt,
                        unsigned int *count, unsigned int *num_planes,
-                       unsigned long sizes[], void *alloc_ctxs[])
+                       unsigned int sizes[], void *alloc_ctxs[])
 {
        struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -243,7 +220,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
                        *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
        }
 
-       dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]);
+       dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
 
        return 0;
 }
@@ -267,6 +244,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
        unsigned long top1, top2;
        unsigned long bottom1, bottom2;
        u32 status;
+       bool planar;
        int ret = 0;
 
        /*
@@ -312,19 +290,31 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
                bottom2 = CDBCR;
        }
 
-       phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0);
-
-       ceu_write(pcdev, top1, phys_addr_top);
-       if (V4L2_FIELD_NONE != pcdev->field) {
-               phys_addr_bottom = phys_addr_top + icd->user_width;
-               ceu_write(pcdev, bottom1, phys_addr_bottom);
-       }
+       phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
 
        switch (icd->current_fmt->host_fmt->fourcc) {
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
        case V4L2_PIX_FMT_NV16:
        case V4L2_PIX_FMT_NV61:
+               planar = true;
+               break;
+       default:
+               planar = false;
+       }
+
+       ceu_write(pcdev, top1, phys_addr_top);
+       if (V4L2_FIELD_NONE != pcdev->field) {
+               if (planar)
+                       phys_addr_bottom = phys_addr_top + icd->user_width;
+               else
+                       phys_addr_bottom = phys_addr_top +
+                               soc_mbus_bytes_per_line(icd->user_width,
+                                                       icd->current_fmt->host_fmt);
+               ceu_write(pcdev, bottom1, phys_addr_bottom);
+       }
+
+       if (planar) {
                phys_addr_top += icd->user_width *
                        icd->user_height;
                ceu_write(pcdev, top2, phys_addr_top);
@@ -628,22 +618,22 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
        left_offset     = cam->ceu_left;
        top_offset      = cam->ceu_top;
 
-       /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */
+       WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+       width = icd->user_width;
+
        if (pcdev->image_mode) {
                in_width = cam->width;
                if (!pcdev->is_16bit) {
                        in_width *= 2;
                        left_offset *= 2;
                }
-               width = icd->user_width;
-               cdwdr_width = icd->user_width;
+               cdwdr_width = width;
        } else {
-               int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+               int bytes_per_line = soc_mbus_bytes_per_line(width,
                                                icd->current_fmt->host_fmt);
                unsigned int w_factor;
 
-               width = icd->user_width;
-
                switch (icd->current_fmt->host_fmt->packing) {
                case SOC_MBUS_PACKING_2X8_PADHI:
                        w_factor = 2;
@@ -653,10 +643,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
                }
 
                in_width = cam->width * w_factor;
-               left_offset = left_offset * w_factor;
+               left_offset *= w_factor;
 
                if (bytes_per_line < 0)
-                       cdwdr_width = icd->user_width;
+                       cdwdr_width = width;
                else
                        cdwdr_width = bytes_per_line;
        }
@@ -664,7 +654,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
        height = icd->user_height;
        in_height = cam->height;
        if (V4L2_FIELD_NONE != pcdev->field) {
-               height /= 2;
+               height = (height / 2) & ~3;
                in_height /= 2;
                top_offset /= 2;
                cdwdr_width *= 2;
@@ -686,6 +676,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 
        ceu_write(pcdev, CAMOR, camor);
        ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+       /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
        ceu_write(pcdev, CFSZR, (height << 16) | width);
        ceu_write(pcdev, CDWDR, cdwdr_width);
 }
@@ -723,66 +714,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
                ceu_write(pcdev, CAPSR, capsr);
 }
 
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+                                          struct soc_camera_device *icd)
+{
+       if (pcdev->csi2_pdev) {
+               struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+               if (csi2_sd && csi2_sd->grp_id == (u32)icd)
+                       return csi2_sd;
+       }
+
+       return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER |      \
+               V4L2_MBUS_PCLK_SAMPLE_RISING |  \
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH |   \
+               V4L2_MBUS_HSYNC_ACTIVE_LOW |    \
+               V4L2_MBUS_VSYNC_ACTIVE_HIGH |   \
+               V4L2_MBUS_VSYNC_ACTIVE_LOW |    \
+               V4L2_MBUS_DATA_ACTIVE_HIGH)
+
 /* Capture is not running, no interrupts, no locking needed */
 static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
                                       __u32 pixfmt)
 {
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
-       int ret;
-       unsigned long camera_flags, common_flags, value;
-       int yuv_lineskip;
+       struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
        struct sh_mobile_ceu_cam *cam = icd->host_priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long value, common_flags = CEU_BUS_FLAGS;
        u32 capsr = capture_save_reset(pcdev);
+       unsigned int yuv_lineskip;
+       int ret;
 
-       camera_flags = icd->ops->query_bus_param(icd);
-       common_flags = soc_camera_bus_param_compatible(camera_flags,
-                                                      make_bus_param(pcdev));
-       if (!common_flags)
-               return -EINVAL;
+       /*
+        * If the client doesn't implement g_mbus_config, we just use our
+        * platform data
+        */
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         common_flags);
+               if (!common_flags)
+                       return -EINVAL;
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       }
 
        /* Make choises, based on platform preferences */
-       if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-           (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
                if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
-                       common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
                else
-                       common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
        }
 
-       if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-           (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+       if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
                if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
-                       common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
                else
-                       common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
        }
 
-       ret = icd->ops->set_bus_param(icd, common_flags);
-       if (ret < 0)
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD)
                return ret;
 
-       switch (common_flags & SOCAM_DATAWIDTH_MASK) {
-       case SOCAM_DATAWIDTH_8:
-               pcdev->is_16bit = 0;
-               break;
-       case SOCAM_DATAWIDTH_16:
+       if (icd->current_fmt->host_fmt->bits_per_sample > 8)
                pcdev->is_16bit = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
+       else
+               pcdev->is_16bit = 0;
 
        ceu_write(pcdev, CRCNTR, 0);
        ceu_write(pcdev, CRCMPR, 0);
 
        value = 0x00000010; /* data fetch by default */
-       yuv_lineskip = 0;
+       yuv_lineskip = 0x10;
 
        switch (icd->current_fmt->host_fmt->fourcc) {
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
-               yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+               /* convert 4:2:2 -> 4:2:0 */
+               yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
                /* fall-through */
        case V4L2_PIX_FMT_NV16:
        case V4L2_PIX_FMT_NV61:
@@ -808,8 +826,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
            icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
                value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
 
-       value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
-       value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+       value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+       value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
        value |= pcdev->is_16bit ? 1 << 12 : 0;
 
        /* CSI2 mode */
@@ -852,9 +870,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
         * using 7 we swap the data bytes to match the incoming order:
         * D0, D1, D2, D3, D4, D5, D6, D7
         */
-       value = 0x00000017;
-       if (yuv_lineskip)
-               value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+       value = 0x00000007 | yuv_lineskip;
 
        ceu_write(pcdev, CDOCR, value);
        ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
@@ -875,13 +891,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
 {
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
-       unsigned long camera_flags, common_flags;
+       struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+       unsigned long common_flags = CEU_BUS_FLAGS;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       int ret;
 
-       camera_flags = icd->ops->query_bus_param(icd);
-       common_flags = soc_camera_bus_param_compatible(camera_flags,
-                                                      make_bus_param(pcdev));
-       if (!common_flags || buswidth > 16 ||
-           (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret)
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         common_flags);
+       else if (ret != -ENOIOCTLCMD)
+               return ret;
+
+       if (!common_flags || buswidth > 16)
                return -EINVAL;
 
        return 0;
@@ -891,26 +913,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
        {
                .fourcc                 = V4L2_PIX_FMT_NV12,
                .name                   = "NV12",
-               .bits_per_sample        = 12,
-               .packing                = SOC_MBUS_PACKING_NONE,
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_1_5X8,
                .order                  = SOC_MBUS_ORDER_LE,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV21,
                .name                   = "NV21",
-               .bits_per_sample        = 12,
-               .packing                = SOC_MBUS_PACKING_NONE,
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_1_5X8,
                .order                  = SOC_MBUS_ORDER_LE,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV16,
                .name                   = "NV16",
-               .bits_per_sample        = 16,
-               .packing                = SOC_MBUS_PACKING_NONE,
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_2X8_PADHI,
                .order                  = SOC_MBUS_ORDER_LE,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV61,
                .name                   = "NV61",
-               .bits_per_sample        = 16,
-               .packing                = SOC_MBUS_PACKING_NONE,
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_2X8_PADHI,
                .order                  = SOC_MBUS_ORDER_LE,
        },
 };
@@ -919,6 +941,8 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
 static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
 {
        return  fmt->packing == SOC_MBUS_PACKING_NONE ||
+               (fmt->bits_per_sample == 8 &&
+                fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
                (fmt->bits_per_sample == 8 &&
                 fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
                (fmt->bits_per_sample > 8 &&
@@ -927,6 +951,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
 
 static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
 
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+       return container_of(ctrl->handler, struct soc_camera_device,
+                                                       ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+       switch (ctrl->id) {
+       case V4L2_CID_SHARPNESS:
+               switch (icd->current_fmt->host_fmt->fourcc) {
+               case V4L2_PIX_FMT_NV12:
+               case V4L2_PIX_FMT_NV21:
+               case V4L2_PIX_FMT_NV16:
+               case V4L2_PIX_FMT_NV61:
+                       ceu_write(pcdev, CLFCR, !ctrl->val);
+                       return 0;
+               }
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+       .s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
 static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
                                     struct soc_camera_format_xlate *xlate)
 {
@@ -952,6 +1008,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
        }
 
        if (!pcdev->pdata->csi2) {
+               /* Are there any restrictions in the CSI-2 case? */
                ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
                if (ret < 0)
                        return 0;
@@ -962,6 +1019,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
                struct v4l2_rect rect;
                int shift = 0;
 
+               /* Add our control */
+               v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+                                 V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+               if (icd->ctrl_handler.error)
+                       return icd->ctrl_handler.error;
+
                /* FIXME: subwindow is lost between close / open */
 
                /* Cache current client geometry */
@@ -1004,9 +1067,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
                cam->width      = mf.width;
                cam->height     = mf.height;
 
-               cam->width      = mf.width;
-               cam->height     = mf.height;
-
                icd->host_priv = cam;
        } else {
                cam = icd->host_priv;
@@ -1278,6 +1338,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
        unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
        unsigned int max_width, max_height;
        struct v4l2_cropcap cap;
+       bool ceu_1to1;
        int ret;
 
        ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
@@ -1287,7 +1348,14 @@ static int client_s_fmt(struct soc_camera_device *icd,
 
        dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
 
-       if ((width == mf->width && height == mf->height) || !ceu_can_scale)
+       if (width == mf->width && height == mf->height) {
+               /* Perfect! The client has done it all. */
+               ceu_1to1 = true;
+               goto update_cache;
+       }
+
+       ceu_1to1 = false;
+       if (!ceu_can_scale)
                goto update_cache;
 
        cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1327,7 +1395,10 @@ update_cache:
        if (ret < 0)
                return ret;
 
-       update_subrect(cam);
+       if (ceu_1to1)
+               cam->subrect = cam->rect;
+       else
+               update_subrect(cam);
 
        return 0;
 }
@@ -1414,7 +1485,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
        capsr = capture_save_reset(pcdev);
        dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
 
-       /* 1. - 2. Apply iterative camera S_CROP for new input window. */
+       /*
+        * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+        * actual camera rectangle.
+        */
        ret = client_s_crop(icd, a, &cam_crop);
        if (ret < 0)
                return ret;
@@ -1498,8 +1572,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
                ceu_write(pcdev, CFLCR, cflcr);
        }
 
-       icd->user_width  = out_width;
-       icd->user_height = out_height;
+       icd->user_width  = out_width & ~3;
+       icd->user_height = out_height & ~3;
+       /* Offsets are applied at the CEU scaling filter input */
        cam->ceu_left    = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
        cam->ceu_top     = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
 
@@ -1538,7 +1613,7 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
  * CEU crop, mapped backed onto the client input (subrect).
  */
 static void calculate_client_output(struct soc_camera_device *icd,
-               struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+               const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
 {
        struct sh_mobile_ceu_cam *cam = icd->host_priv;
        struct device *dev = icd->parent;
@@ -1574,8 +1649,8 @@ static void calculate_client_output(struct soc_camera_device *icd,
        dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
 
        /*
-        * 4. Calculate client output window by applying combined scales to real
-        *    input window.
+        * 4. Calculate desired client output window by applying combined scales
+        *    to client (real) input window.
         */
        mf->width       = scale_down(cam->rect.width, scale_h);
        mf->height      = scale_down(cam->rect.height, scale_v);
@@ -1600,8 +1675,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
        bool image_mode;
        enum v4l2_field field;
 
-       dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
-
        switch (pix->field) {
        default:
                pix->field = V4L2_FIELD_NONE;
@@ -1622,8 +1695,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
                return -EINVAL;
        }
 
-       /* 1.-4. Calculate client output geometry */
-       calculate_client_output(icd, &f->fmt.pix, &mf);
+       /* 1.-4. Calculate desired client output geometry */
+       calculate_client_output(icd, pix, &mf);
        mf.field        = pix->field;
        mf.colorspace   = pix->colorspace;
        mf.code         = xlate->code;
@@ -1639,6 +1712,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
                image_mode = false;
        }
 
+       dev_info(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+               pix->width, pix->height);
+
        dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
 
        /* 5. - 9. */
@@ -1700,6 +1776,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
        pcdev->field = field;
        pcdev->image_mode = image_mode;
 
+       /* CFSZR requirement */
+       pix->width      &= ~3;
+       pix->height     &= ~3;
+
        return 0;
 }
 
@@ -1725,7 +1805,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
 
        /* FIXME: calculate using depth and bus width */
 
-       v4l_bound_align_image(&pix->width, 2, 2560, 1,
+       /* CFSZR requires height and width to be 4-pixel aligned */
+       v4l_bound_align_image(&pix->width, 2, 2560, 2,
                              &pix->height, 4, 1920, 2, 0);
 
        width = pix->width;
@@ -1778,6 +1859,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
                        pix->height = height;
        }
 
+       pix->width      &= ~3;
+       pix->height     &= ~3;
+
        dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
                __func__, ret, pix->pixelformat, pix->width, pix->height);
 
@@ -1824,8 +1908,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
                             out_height != f.fmt.pix.height))
                        ret = -EINVAL;
                if (!ret) {
-                       icd->user_width         = out_width;
-                       icd->user_height        = out_height;
+                       icd->user_width         = out_width & ~3;
+                       icd->user_height        = out_height & ~3;
                        ret = sh_mobile_ceu_set_bus_param(icd,
                                        icd->current_fmt->host_fmt->fourcc);
                }
@@ -1869,55 +1953,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
        return vb2_queue_init(q);
 }
 
-static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
-                                 struct v4l2_control *ctrl)
-{
-       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-       struct sh_mobile_ceu_dev *pcdev = ici->priv;
-       u32 val;
-
-       switch (ctrl->id) {
-       case V4L2_CID_SHARPNESS:
-               val = ceu_read(pcdev, CLFCR);
-               ctrl->value = val ^ 1;
-               return 0;
-       }
-       return -ENOIOCTLCMD;
-}
-
-static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
-                                 struct v4l2_control *ctrl)
-{
-       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-       struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
-       switch (ctrl->id) {
-       case V4L2_CID_SHARPNESS:
-               switch (icd->current_fmt->host_fmt->fourcc) {
-               case V4L2_PIX_FMT_NV12:
-               case V4L2_PIX_FMT_NV21:
-               case V4L2_PIX_FMT_NV16:
-               case V4L2_PIX_FMT_NV61:
-                       ceu_write(pcdev, CLFCR, !ctrl->value);
-                       return 0;
-               }
-               return -EINVAL;
-       }
-       return -ENOIOCTLCMD;
-}
-
-static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
-       {
-               .id             = V4L2_CID_SHARPNESS,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-               .name           = "Low-pass filter",
-               .minimum        = 0,
-               .maximum        = 1,
-               .step           = 1,
-               .default_value  = 0,
-       },
-};
-
 static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
        .owner          = THIS_MODULE,
        .add            = sh_mobile_ceu_add_device,
@@ -1929,14 +1964,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
        .set_livecrop   = sh_mobile_ceu_set_livecrop,
        .set_fmt        = sh_mobile_ceu_set_fmt,
        .try_fmt        = sh_mobile_ceu_try_fmt,
-       .set_ctrl       = sh_mobile_ceu_set_ctrl,
-       .get_ctrl       = sh_mobile_ceu_get_ctrl,
        .poll           = sh_mobile_ceu_poll,
        .querycap       = sh_mobile_ceu_querycap,
        .set_bus_param  = sh_mobile_ceu_set_bus_param,
        .init_videobuf2 = sh_mobile_ceu_init_videobuf,
-       .controls       = sh_mobile_ceu_controls,
-       .num_controls   = ARRAY_SIZE(sh_mobile_ceu_controls),
 };
 
 struct bus_wait {