diff options
author | Seung-Woo Kim <sw0312.kim@samsung.com> | 2013-10-22 16:44:13 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-11-18 11:44:59 +0900 |
commit | 8136406f9590001cb012b4019ee088617e657530 (patch) | |
tree | 1c4487b5f544c93baac20478f241c7a37ca0ad3a /drivers/gpu | |
parent | 8c5a60dca6bec6013c4b65d887e211acdc8c0663 (diff) | |
download | linux-3.10-8136406f9590001cb012b4019ee088617e657530.tar.gz linux-3.10-8136406f9590001cb012b4019ee088617e657530.tar.bz2 linux-3.10-8136406f9590001cb012b4019ee088617e657530.zip |
drm/exynos: fix to calculate offset of each plane for ipp fimc
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>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimc.c | 124 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_ipp.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_ipp.h | 2 |
3 files changed, 138 insertions, 2 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 4ba19885d31..2959c2e9742 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -409,6 +409,115 @@ 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_NV12MT: + ofs[0] = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize, 32), SZ_8K); + ofs[1] = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize >> 1, 32), SZ_8K); + if (*base[0] && *base[1]) { + if (size[0] + size[1] >= ofs[0] + ofs[1]) + bypass = true; + else { + ofs[0] = ALIGN(sz->hsize * sz->vsize, + PAGE_SIZE); + ofs[1] = ALIGN(ofs[0] >> 1, PAGE_SIZE); + 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) { @@ -705,6 +814,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"); @@ -725,6 +835,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_CIIYSA(0)); @@ -1205,6 +1321,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"); @@ -1225,6 +1342,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)); @@ -1646,6 +1768,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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index fb3c791760a..fb2f1bdfc10 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -687,6 +687,7 @@ static struct drm_exynos_ipp_mem_node struct drm_exynos_ipp_mem_node *m_node; struct drm_exynos_ipp_buf_info buf_info; void *addr; + unsigned long size; int i; mutex_lock(&c_node->mem_lock); @@ -721,11 +722,20 @@ static struct drm_exynos_ipp_mem_node goto err_clear; } + size = exynos_drm_gem_get_size(drm_dev, + qbuf->handle[i], file); + if (!size) { + DRM_ERROR("failed to get size.\n"); + goto err_clear; + } + buf_info.handles[i] = qbuf->handle[i]; buf_info.base[i] = *(dma_addr_t *) addr; - DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]\n", + buf_info.size[i] = (uint64_t) size; + DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]sz[%d]\n", __func__, i, buf_info.base[i], - (int)buf_info.handles[i]); + (int)buf_info.handles[i], + (int)buf_info.size[i]); } } diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h index 4cadbea7dbd..9af69b3ca81 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -85,10 +85,12 @@ struct drm_exynos_ipp_cmd_node { * * @gem_objs: Y, Cb, Cr each gem object. * @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]; }; /* |