Merge branches 'fixes' and 'fixes2' into devel-late
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / media / video / sh_mobile_ceu_camera.c
index 424dfac..0baaf94 100644 (file)
@@ -210,27 +210,33 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
        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);
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
-       int bytes_per_line;
-       unsigned int height;
 
        if (fmt) {
                const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
                                                                fmt->fmt.pix.pixelformat);
+               unsigned int bytes_per_line;
+               int ret;
+
                if (!xlate)
                        return -EINVAL;
-               bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
-                                                        xlate->host_fmt);
-               height = fmt->fmt.pix.height;
+
+               ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+                                             xlate->host_fmt);
+               if (ret < 0)
+                       return ret;
+
+               bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+               ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+                                         fmt->fmt.pix.height);
+               if (ret < 0)
+                       return ret;
+
+               sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
        } else {
                /* Called from VIDIOC_REQBUFS or in compatibility mode */
-               bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
-                                               icd->current_fmt->host_fmt);
-               height = icd->user_height;
+               sizes[0] = icd->sizeimage;
        }
-       if (bytes_per_line < 0)
-               return bytes_per_line;
-
-       sizes[0] = bytes_per_line * height;
 
        alloc_ctxs[0] = pcdev->alloc_ctx;
 
@@ -336,21 +342,15 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 
        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);
+               phys_addr_bottom = phys_addr_top + icd->bytesperline;
                ceu_write(pcdev, bottom1, phys_addr_bottom);
        }
 
        if (planar) {
-               phys_addr_top += icd->user_width *
-                       icd->user_height;
+               phys_addr_top += icd->bytesperline * icd->user_height;
                ceu_write(pcdev, top2, phys_addr_top);
                if (V4L2_FIELD_NONE != pcdev->field) {
-                       phys_addr_bottom = phys_addr_top + icd->user_width;
+                       phys_addr_bottom = phys_addr_top + icd->bytesperline;
                        ceu_write(pcdev, bottom2, phys_addr_bottom);
                }
        }
@@ -377,13 +377,8 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
        struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
        unsigned long size;
-       int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
-                                               icd->current_fmt->host_fmt);
 
-       if (bytes_per_line < 0)
-               goto error;
-
-       size = icd->user_height * bytes_per_line;
+       size = icd->sizeimage;
 
        if (vb2_plane_size(vb, 0) < size) {
                dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
@@ -682,10 +677,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
                        in_width *= 2;
                        left_offset *= 2;
                }
-               cdwdr_width = width;
        } else {
-               int bytes_per_line = soc_mbus_bytes_per_line(width,
-                                               icd->current_fmt->host_fmt);
                unsigned int w_factor;
 
                switch (icd->current_fmt->host_fmt->packing) {
@@ -698,13 +690,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 
                in_width = cam->width * w_factor;
                left_offset *= w_factor;
-
-               if (bytes_per_line < 0)
-                       cdwdr_width = width;
-               else
-                       cdwdr_width = bytes_per_line;
        }
 
+       cdwdr_width = icd->bytesperline;
+
        height = icd->user_height;
        in_height = cam->height;
        if (V4L2_FIELD_NONE != pcdev->field) {
@@ -881,11 +870,13 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 
        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 */
-       if (pcdev->pdata->csi2)
+       if (pcdev->pdata->csi2) /* CSI2 mode */
                value |= 3 << 12;
+       else if (pcdev->is_16bit)
+               value |= 1 << 12;
+       else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+               value |= 2 << 12;
 
        ceu_write(pcdev, CAMCR, value);
 
@@ -964,24 +955,28 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
                .bits_per_sample        = 8,
                .packing                = SOC_MBUS_PACKING_1_5X8,
                .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV21,
                .name                   = "NV21",
                .bits_per_sample        = 8,
                .packing                = SOC_MBUS_PACKING_1_5X8,
                .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV16,
                .name                   = "NV16",
                .bits_per_sample        = 8,
                .packing                = SOC_MBUS_PACKING_2X8_PADHI,
                .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PLANAR_Y_C,
        }, {
                .fourcc                 = V4L2_PIX_FMT_NV61,
                .name                   = "NV61",
                .bits_per_sample        = 8,
                .packing                = SOC_MBUS_PACKING_2X8_PADHI,
                .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PLANAR_Y_C,
        },
 };
 
@@ -1845,6 +1840,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
        return 0;
 }
 
+#define CEU_CHDW_MAX   8188U   /* Maximum line stride */
+
 static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
                                 struct v4l2_format *f)
 {
@@ -1863,8 +1860,12 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
 
        xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
        if (!xlate) {
-               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
-               return -EINVAL;
+               xlate = icd->current_fmt;
+               dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
+                       pixfmt, xlate->host_fmt->fourcc);
+               pixfmt = xlate->host_fmt->fourcc;
+               pix->pixelformat = pixfmt;
+               pix->colorspace = icd->colorspace;
        }
 
        /* FIXME: calculate using depth and bus width */
@@ -1923,10 +1924,20 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
                        pix->width = width;
                if (mf.height > height)
                        pix->height = height;
+
+               pix->bytesperline = max(pix->bytesperline, pix->width);
+               pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX);
+               pix->bytesperline &= ~3;
+               break;
+
+       default:
+               /* Configurable stride isn't supported in pass-through mode. */
+               pix->bytesperline  = 0;
        }
 
        pix->width      &= ~3;
        pix->height     &= ~3;
+       pix->sizeimage  = 0;
 
        dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
                __func__, ret, pix->pixelformat, pix->width, pix->height);
@@ -2145,6 +2156,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
        pcdev->ici.nr = pdev->id;
        pcdev->ici.drv_name = dev_name(&pdev->dev);
        pcdev->ici.ops = &sh_mobile_ceu_host_ops;
+       pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE;
 
        pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
        if (IS_ERR(pcdev->alloc_ctx)) {