drm/exynos: fix to calculate offset of each plane for ipp fimc
authorSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 4 Mar 2015 05:05:02 +0000 (14:05 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 14 Dec 2016 04:44:07 +0000 (13:44 +0900)
NV12 and YUV420 formats are need to calculate offset of each plane
for ipp fimc in a gem buffer. Without proper offset, only Y plane
can be processed, so result shows green frame.
This patch fixes to calculate offset for cbcr planes for NV12 and
YUV420 formats.

Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
drivers/gpu/drm/exynos/exynos_drm_fimc.c
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/exynos/exynos_drm_ipp.h

index 0d3a2ae..292683d 100644 (file)
@@ -403,6 +403,97 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
        fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
 }
 
+static int fimc_set_planar_addr(struct drm_exynos_ipp_buf_info *buf_info,
+                               u32 fmt, struct drm_exynos_sz *sz)
+{
+       dma_addr_t *base[EXYNOS_DRM_PLANAR_MAX];
+       uint64_t size[EXYNOS_DRM_PLANAR_MAX];
+       uint64_t ofs[EXYNOS_DRM_PLANAR_MAX];
+       bool bypass = false;
+       uint64_t tsize = 0;
+       int i;
+
+       for_each_ipp_planar(i) {
+               base[i] = &buf_info->base[i];
+               size[i] = buf_info->size[i];
+               ofs[i] = 0;
+               tsize += size[i];
+       }
+
+       if (!tsize) {
+               DRM_INFO("%s:failed to get buffer size.\n", __func__);
+               return 0;
+       }
+
+       switch (fmt) {
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV61:
+               ofs[0] = sz->hsize * sz->vsize;
+               ofs[1] = ofs[0] >> 1;
+               if (*base[0] && *base[1]) {
+                       if (size[0] + size[1] < ofs[0] + ofs[1])
+                               goto err_info;
+                       bypass = true;
+               }
+               break;
+       case DRM_FORMAT_YUV410:
+       case DRM_FORMAT_YVU410:
+       case DRM_FORMAT_YUV411:
+       case DRM_FORMAT_YVU411:
+       case DRM_FORMAT_YUV420:
+       case DRM_FORMAT_YVU420:
+       case DRM_FORMAT_YUV422:
+       case DRM_FORMAT_YVU422:
+       case DRM_FORMAT_YUV444:
+       case DRM_FORMAT_YVU444:
+               ofs[0] = sz->hsize * sz->vsize;
+               ofs[1] = ofs[2] = ofs[0] >> 2;
+               if (*base[0] && *base[1] && *base[2]) {
+                       if (size[0]+size[1]+size[2] < ofs[0]+ofs[1]+ofs[2])
+                               goto err_info;
+                       bypass = true;
+               }
+               break;
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+               ofs[0] = sz->hsize * sz->vsize << 2;
+               if (*base[0]) {
+                       if (size[0] < ofs[0])
+                               goto err_info;
+               }
+               bypass = true;
+               break;
+       default:
+               bypass = true;
+               break;
+       }
+
+       if (!bypass) {
+               *base[1] = *base[0] + ofs[0];
+               if (ofs[1] && ofs[2])
+                       *base[2] = *base[1] + ofs[1];
+       }
+
+       DRM_DEBUG_KMS("%s:y[0x%x],cb[0x%x],cr[0x%x]\n", __func__,
+               *base[0], *base[1], *base[2]);
+
+       return 0;
+
+err_info:
+       DRM_ERROR("invalid size for fmt[0x%x]\n", fmt);
+
+       for_each_ipp_planar(i) {
+               base[i] = &buf_info->base[i];
+               size[i] = buf_info->size[i];
+
+               DRM_ERROR("buf[%d] - base[0x%x] sz[%llu] ofs[%llu]\n",
+                       i, *base[i], size[i], ofs[i]);
+       }
+
+       return -EINVAL;
+}
 
 static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
 {
@@ -691,6 +782,7 @@ static int fimc_src_set_addr(struct device *dev,
        struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
        struct drm_exynos_ipp_property *property;
        struct drm_exynos_ipp_config *config;
+       int ret;
 
        if (!c_node) {
                DRM_ERROR("failed to get c_node.\n");
@@ -711,6 +803,12 @@ static int fimc_src_set_addr(struct device *dev,
        switch (buf_type) {
        case IPP_BUF_ENQUEUE:
                config = &property->config[EXYNOS_DRM_OPS_SRC];
+               ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz);
+               if (ret) {
+                       dev_err(dev, "failed to set plane src addr.\n");
+                       return ret;
+               }
+
                fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
                        EXYNOS_CIIYSA0);
 
@@ -1153,6 +1251,7 @@ static int fimc_dst_set_addr(struct device *dev,
        struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
        struct drm_exynos_ipp_property *property;
        struct drm_exynos_ipp_config *config;
+       int ret;
 
        if (!c_node) {
                DRM_ERROR("failed to get c_node.\n");
@@ -1173,6 +1272,11 @@ static int fimc_dst_set_addr(struct device *dev,
        switch (buf_type) {
        case IPP_BUF_ENQUEUE:
                config = &property->config[EXYNOS_DRM_OPS_DST];
+               ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz);
+               if (ret) {
+                       dev_err(dev, "failed to set plane dst addr.\n");
+                       return ret;
+               }
 
                fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
                        EXYNOS_CIOYSA(buf_id));
@@ -1584,6 +1688,8 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        /* reset sequence */
        fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
 
+       fimc_clear_addr(ctx);
+
        /* Scaler disable */
        fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
 
index ac35625..4a7a80d 100644 (file)
@@ -574,6 +574,7 @@ static struct drm_exynos_ipp_mem_node
                /* get dma address by handle */
                if (qbuf->handle[i]) {
                        dma_addr_t *addr;
+                       unsigned long size;
 
                        addr = exynos_drm_gem_get_dma_addr(drm_dev,
                                        qbuf->handle[i], c_node->filp);
@@ -583,10 +584,20 @@ static struct drm_exynos_ipp_mem_node
                                return ERR_PTR(-EFAULT);
                        }
 
+                       size = exynos_drm_gem_get_size(drm_dev,
+                                       qbuf->handle[i], c_node->filp);
+                       if (!size) {
+                               DRM_ERROR("failed to get size.\n");
+                               ipp_put_mem_node(drm_dev, c_node, m_node);
+                               return ERR_PTR(-EFAULT);
+                       }
+
                        buf_info->handles[i] = qbuf->handle[i];
                        buf_info->base[i] = *addr;
-                       DRM_DEBUG_KMS("i[%d]base[%pad]hd[0x%lx]\n", i,
-                                     &buf_info->base[i], buf_info->handles[i]);
+                       buf_info->size[i] = (uint64_t)size;
+                       DRM_DEBUG_KMS("i[%d]base[%pad]hd[0x%lx]sz[%llx]\n", i,
+                                     &buf_info->base[i], buf_info->handles[i],
+                                     buf_info->size[i]);
                }
        }
 
index 2a61547..d4f0b58 100644 (file)
@@ -85,10 +85,12 @@ struct drm_exynos_ipp_cmd_node {
  *
  * @handles: Y, Cb, Cr each gem object handle.
  * @base: Y, Cb, Cr each planar address.
+ * @size: Y, Cb, Cr each planar size.
  */
 struct drm_exynos_ipp_buf_info {
        unsigned long   handles[EXYNOS_DRM_PLANAR_MAX];
        dma_addr_t      base[EXYNOS_DRM_PLANAR_MAX];
+       uint64_t        size[EXYNOS_DRM_PLANAR_MAX];
 };
 
 /*