summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/exynos
diff options
context:
space:
mode:
authorSeung-Woo Kim <sw0312.kim@samsung.com>2013-10-22 16:44:13 +0900
committerChanho Park <chanho61.park@samsung.com>2014-11-18 11:44:59 +0900
commit8136406f9590001cb012b4019ee088617e657530 (patch)
tree1c4487b5f544c93baac20478f241c7a37ca0ad3a /drivers/gpu/drm/exynos
parent8c5a60dca6bec6013c4b65d887e211acdc8c0663 (diff)
downloadlinux-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/drm/exynos')
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c124
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c14
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.h2
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];
};
/*