From patchwork Mon Nov 30 13:53:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 7725781 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 82B45BEEE1 for ; Mon, 30 Nov 2015 13:54:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 96D6720570 for ; Mon, 30 Nov 2015 13:54:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 862802054D for ; Mon, 30 Nov 2015 13:54:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754320AbbK3NyD (ORCPT ); Mon, 30 Nov 2015 08:54:03 -0500 Received: from mailout3.w1.samsung.com ([210.118.77.13]:49874 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754218AbbK3NyA (ORCPT ); Mon, 30 Nov 2015 08:54:00 -0500 Received: from eucpsbgm1.samsung.com (unknown [203.254.199.244]) by mailout3.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0NYM009XVRXXQFB0@mailout3.w1.samsung.com> for linux-samsung-soc@vger.kernel.org; Mon, 30 Nov 2015 13:53:57 +0000 (GMT) X-AuditID: cbfec7f4-f79026d00000418a-58-565c54f44960 Received: from eusync2.samsung.com ( [203.254.199.212]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id 17.CC.16778.4F45C565; Mon, 30 Nov 2015 13:53:56 +0000 (GMT) Received: from amdc1339.digital.local ([106.116.147.30]) by eusync2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0NYM007NRRXFE700@eusync2.samsung.com>; Mon, 30 Nov 2015 13:53:56 +0000 (GMT) From: Marek Szyprowski To: dri-devel@lists.freedesktop.org, linux-samsung-soc@vger.kernel.org Cc: Marek Szyprowski , Inki Dae , Joonyoung Shim , Seung-Woo Kim , Andrzej Hajda , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz , Tobias Jakobi , Gustavo Padovan , Javier Martinez Canillas Subject: [PATCH v2 22/22] drm/exynos: add support for plane rotation, scalling and colospace convesion Date: Mon, 30 Nov 2015 14:53:37 +0100 Message-id: <1448891617-18830-23-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.2 In-reply-to: <1448891617-18830-1-git-send-email-m.szyprowski@samsung.com> References: <1448891617-18830-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrNLMWRmVeSWpSXmKPExsVy+t/xK7pfQmLCDLb26FncWneO1WLjjPWs Fle+vmez2PlgF7vFpPsTWCzevF3DZPHi3kUWi9cvDC1mnN/HZLH2yF12ixmTX7JZtK3+wGqx atcfRgdej52z7rJ73O8+zuTx7xi7x5Z+IG/npL1MHn1bVjF6fN4kF8AexWWTkpqTWZZapG+X wJWxcVpKwa/TjBUzp85na2BctZKxi5GTQ0LAROLcsocsELaYxIV769m6GLk4hASWMkos2L2Z BcJpYpL4vPw+WBWbgKFE19suNhBbRMBNounwTFYQm1ngMLPE4al1ILawQIZEy5IbYDUsAqoS l5fdYgKxeQU8JWas28gEsU1O4v/LFWA2J1D8ydnT7CC2kICHxIYZx5gmMPIuYGRYxSiaWppc UJyUnmuoV5yYW1yal66XnJ+7iRESpF92MC4+ZnWIUYCDUYmHV8IsOkyINbGsuDL3EKMEB7OS CG+gaUyYEG9KYmVValF+fFFpTmrxIUZpDhYlcd65u96HCAmkJ5akZqemFqQWwWSZODilGhht bx/duG6mOuu3q0LSauu+qGhnrX+pVDDNlmG5Y3WOeveS6WbNEs/EBR8J//tw29M6nmWmQSLX fDc2BS3fZUvb/eQOtDie//vl8rYPoTsuREv0yWhOVXj3O079rKfXS9lbto/3/Xol9kleXdTx bfQjs2WTFvMzPMr7ICyQda9t8drN+icelxQrsRRnJBpqMRcVJwIAFJxlxk4CAAA= Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds generic plane rotation property for all supported drivers. This has been implemented with additional help from Exynos IPP (Exynos Image Post-Processing subsystem) with temporary framebuffers. Besides rotation, scaling and color space conversion are also supported. Signed-off-by: Marek Szyprowski --- drivers/gpu/drm/exynos/Kconfig | 8 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 9 + drivers/gpu/drm/exynos/exynos_drm_ipp.c | 154 ++++++++++- drivers/gpu/drm/exynos/exynos_drm_ipp.h | 4 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 28 +- drivers/gpu/drm/exynos/exynos_drm_plane_ipp.c | 369 ++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_plane_ipp.h | 73 +++++ 8 files changed, 641 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_plane_ipp.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_plane_ipp.h diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 83efca941388..e7d414aefbdc 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -104,6 +104,14 @@ config DRM_EXYNOS_IPP help Choose this option if you want to use IPP feature for DRM. +config DRM_EXYNOS_PLANE_IPP + depends on DRM_EXYNOS_IPP + bool "Use IPP framework for implementing unsupported plane properties" + help + Choose this option if you want to let IPP framework to provide plane + properties (like rotation, overlay scaling and more pixel formats), + which are not supported by hardware CRTC drivers. + config DRM_EXYNOS_FIMC bool "FIMC" depends on DRM_EXYNOS_IPP && MFD_SYSCON diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 6496532aaa91..92c3f7cac7a9 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -19,6 +19,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o +exynosdrm-$(CONFIG_DRM_EXYNOS_PLANE_IPP) += exynos_drm_plane_ipp.o exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 670f6d06a2a9..b17e419935db 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -70,6 +70,13 @@ struct exynos_drm_plane_state { unsigned int h_ratio; unsigned int v_ratio; struct drm_framebuffer *fb; + unsigned int rotation; + + unsigned int ipp_needed; + uint32_t ipp_pixel_format; + struct exynos_drm_rect ipp_src; + struct exynos_drm_rect ipp_dst; + struct drm_framebuffer *ipp_fb; }; static inline struct exynos_drm_plane_state * @@ -93,6 +100,8 @@ struct exynos_drm_plane { const struct exynos_drm_plane_config *config; unsigned int zpos; struct drm_framebuffer *pending_fb; + struct drm_framebuffer *ipp_cur_fb; + struct drm_framebuffer *ipp_next_fb; }; #define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0) diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 44a6689e0f4c..231cfbfe036c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -20,6 +20,7 @@ #include #include #include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" #include "exynos_drm_gem.h" #include "exynos_drm_ipp.h" #include "exynos_drm_iommu.h" @@ -1513,7 +1514,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, spin_lock_irqsave(&drm_dev->event_lock, flags); list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); + wake_up(&e->base.file_priv->event_wait); spin_unlock_irqrestore(&drm_dev->event_lock, flags); mutex_unlock(&c_node->event_lock); @@ -1579,6 +1580,157 @@ err_completion: complete(&c_node->start_complete); } +static struct drm_exynos_ipp_mem_node + *ipp_get_internal_mem_node(struct drm_device *drm_dev, + struct drm_exynos_ipp_cmd_node *c_node, + __u32 prop_id, enum drm_exynos_ops_id ops_id, + struct drm_framebuffer *fb) +{ + struct drm_exynos_ipp_mem_node *m_node; + struct drm_exynos_ipp_buf_info *buf_info; + int i; + + m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); + if (!m_node) + return ERR_PTR(-ENOMEM); + + buf_info = &m_node->buf_info; + + m_node->ops_id = ops_id; + m_node->prop_id = prop_id; + INIT_LIST_HEAD(&m_node->list); + + DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]prop_id[%d]\n", (int)m_node, ops_id, prop_id); + + for_each_ipp_planar(i) { + buf_info->obj[i] = NULL; + buf_info->base[i] = exynos_drm_fb_dma_addr(fb, i); + buf_info->size[i] = fb->pitches[i] * fb->height; + } + + mutex_lock(&c_node->mem_lock); + list_add_tail(&m_node->list, &c_node->mem_list[ops_id]); + mutex_unlock(&c_node->mem_lock); + + return m_node; +} + + +static int exynos_drm_ipp_internal_enqueue_buf(struct drm_device *drm_dev, + __u32 prop_id, enum drm_exynos_ops_id ops_id, + struct drm_framebuffer *fb) +{ + struct drm_exynos_ipp_cmd_node *c_node; + struct drm_exynos_ipp_mem_node *m_node; + int ret; + struct drm_exynos_ipp_queue_buf qbuf = { + .ops_id = ops_id, + .buf_type = IPP_BUF_ENQUEUE, + .prop_id = prop_id, + }; + + DRM_DEBUG_KMS("prop_id[%d]ops_id[%s]\n", + prop_id, ops_id ? "dst" : "src"); + + c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, prop_id); + if (!c_node) { + DRM_ERROR("failed to get command node.\n"); + return -ENODEV; + } + + m_node = ipp_get_internal_mem_node(drm_dev, c_node, prop_id, + ops_id, fb); + if (IS_ERR(m_node)) { + DRM_ERROR("failed to get m_node.\n"); + return PTR_ERR(m_node); + } + + if (ops_id == EXYNOS_DRM_OPS_DST) { + ret = ipp_get_event(drm_dev, c_node, &qbuf); + if (ret) { + DRM_ERROR("failed to get event.\n"); + goto err_clean_node; + } + + ret = ipp_queue_buf_with_run(c_node, m_node, &qbuf); + if (ret) { + DRM_ERROR("failed to run command.\n"); + goto err_clean_node; + } + } + + return 0; + +err_clean_node: + DRM_ERROR("clean memory nodes.\n"); + + ipp_clean_queue_buf(drm_dev, c_node, &qbuf); + return ret; +} + +int exynos_ipp_process_internal(struct drm_device *drm_dev, + struct drm_exynos_ipp_config *src_conf, struct drm_framebuffer *src_fb, + struct drm_exynos_ipp_config *dst_conf, struct drm_framebuffer *dst_fb) +{ + int ret; + struct drm_exynos_ipp_property property = { + .config = { + *src_conf, + *dst_conf, + }, + .cmd = IPP_CMD_M2M, + }; + struct drm_exynos_ipp_cmd_ctrl ctrl = { + /* .prop_id */ + .ctrl = IPP_CTRL_PLAY, + }; + struct drm_exynos_ipp_cmd_ctrl ctrl_stop = { + /* .prop_id */ + .ctrl = IPP_CTRL_STOP, + }; + struct drm_file virt_file = { }; + + INIT_LIST_HEAD(&virt_file.lhead); + INIT_LIST_HEAD(&virt_file.fbs); + mutex_init(&virt_file.fbs_lock); + INIT_LIST_HEAD(&virt_file.blobs); + INIT_LIST_HEAD(&virt_file.event_list); + init_waitqueue_head(&virt_file.event_wait); + virt_file.event_space = 4096; + virt_file.uid = current_euid(); + virt_file.pid = get_pid(task_pid(current)); + virt_file.authenticated = capable(CAP_SYS_ADMIN); + virt_file.lock_count = 0; + + ret = exynos_drm_ipp_set_property(drm_dev, &property, &virt_file); + if (ret) + return ret; + + ctrl.prop_id = property.prop_id; + ctrl_stop.prop_id = property.prop_id; + + ret = exynos_drm_ipp_internal_enqueue_buf(drm_dev, property.prop_id, + EXYNOS_DRM_OPS_SRC, src_fb); + if (ret) + goto cleanup; + + ret = exynos_drm_ipp_internal_enqueue_buf(drm_dev, property.prop_id, + EXYNOS_DRM_OPS_DST, dst_fb); + if (ret) + goto cleanup; + + ret = exynos_drm_ipp_cmd_ctrl(drm_dev, &ctrl, &virt_file); + if (ret) + goto cleanup; + + wait_event(virt_file.event_wait, !list_empty(&virt_file.event_list)); + +cleanup: + exynos_drm_ipp_cmd_ctrl(drm_dev, &ctrl_stop, &virt_file); + + return ret; +} + static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) { struct exynos_drm_ippdrv *ippdrv; diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h index 1dc13bf57b16..7e95437edecb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -24,6 +24,10 @@ #define IPP_GET_LCD_HEIGHT _IOR('F', 303, int) #define IPP_SET_WRITEBACK _IOW('F', 304, u32) +int exynos_ipp_process_internal(struct drm_device *drm_dev, + struct drm_exynos_ipp_config *src_conf, struct drm_framebuffer *src_fb, + struct drm_exynos_ipp_config *dst_conf, struct drm_framebuffer *dst_fb); + /* definition of state */ enum drm_exynos_ipp_state { IPP_STATE_IDLE, diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 8a1242b5a938..34ed1d6b8184 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -19,6 +19,7 @@ #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" #include "exynos_drm_plane.h" +#include "exynos_drm_plane_ipp.h" /* * This function is to get X or Y size shown via screen. This needs length and @@ -56,7 +57,8 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last) return size; } -static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) +static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state, + const struct exynos_drm_plane_config *config) { struct drm_plane_state *state = &exynos_state->base; @@ -85,6 +87,9 @@ static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) src_w = state->src_w >> 16; src_h = state->src_h >> 16; + exynos_plane_ipp_setup(exynos_state, config, &src_x, &src_y, &src_w, + &src_h, &crtc_w, &crtc_h); + /* set ratio */ exynos_state->h_ratio = (src_w << 16) / crtc_w; exynos_state->v_ratio = (src_h << 16) / crtc_h; @@ -163,6 +168,13 @@ static void exynos_drm_plane_destroy_state(struct drm_plane *plane, { struct exynos_drm_plane_state *old_exynos_state = to_exynos_plane_state(old_state); + struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + /* + * This is the only place in the code, where all temporary objects + * used for processing are no longer used and can be freed. + */ + exynos_plane_ipp_cleanup(exynos_plane, old_exynos_state); + __drm_atomic_helper_plane_destroy_state(plane, old_state); kfree(old_exynos_state); } @@ -174,6 +186,7 @@ static struct drm_plane_funcs exynos_plane_funcs = { .reset = exynos_drm_plane_reset, .atomic_duplicate_state = exynos_drm_plane_duplicate_state, .atomic_destroy_state = exynos_drm_plane_destroy_state, + .set_property = drm_atomic_helper_plane_set_property, }; static int @@ -218,16 +231,21 @@ static int exynos_plane_atomic_check(struct drm_plane *plane, return 0; /* translate state into exynos_state */ - exynos_plane_mode_set(exynos_state); + exynos_plane_mode_set(exynos_state, exynos_plane->config); ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state); - return ret; + if (ret) + return ret; + + return exynos_plane_ipp_check(exynos_plane, exynos_state); } static void exynos_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct drm_plane_state *state = plane->state; + struct exynos_drm_plane_state *exynos_state = + to_exynos_plane_state(state); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc); struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); @@ -237,6 +255,8 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, plane->crtc = state->crtc; exynos_plane->pending_fb = state->fb; + exynos_plane_ipp_update(exynos_plane, exynos_state); + if (exynos_crtc->ops->update_plane) exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); } @@ -308,5 +328,5 @@ int exynos_plane_init(struct drm_device *dev, exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos); - return 0; + return exynos_plane_ipp_init(dev, exynos_plane); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.c new file mode 100644 index 000000000000..3086fd29f482 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include + +#include +#include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_ipp.h" +#include "exynos_drm_plane.h" +#include "exynos_drm_plane_ipp.h" +#include "exynos_drm_iommu.h" + +static uint32_t preferred_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_NV12, +}; + +static uint32_t supported_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_NV16, + DRM_FORMAT_NV21, + DRM_FORMAT_NV61, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV420, + DRM_FORMAT_YVU420, +}; + +static bool +exynos_plane_check_format(const struct exynos_drm_plane_config *config, + uint32_t format) +{ + int i; + + for (i = 0; i < config->num_pixel_formats; i++) + if (config->pixel_formats[i] == format) + return true; + return false; +} + +static struct drm_framebuffer *exynos_plane_ipp_alloc_fb(struct drm_device *dev, + struct exynos_drm_plane_state *state) +{ + struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER] = { NULL }; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct drm_framebuffer *fb; + unsigned int size; + unsigned int flags; + int i, cpp; + int num_planes = drm_format_num_planes(state->ipp_pixel_format); + int ret = 0; + + + if (is_drm_iommu_supported(dev)) + flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; + else + flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC; + + mode_cmd.width = state->ipp_dst.x + state->ipp_dst.w; + mode_cmd.height = state->ipp_dst.y + state->ipp_dst.h; + mode_cmd.pixel_format = state->ipp_pixel_format; + + for (i = 0; i < num_planes; i++) { + cpp = drm_format_plane_cpp(state->ipp_pixel_format, i); + mode_cmd.pitches[i] = roundup(mode_cmd.width, + EXYNOS_DRM_PITCH_ALIGN) * cpp; + mode_cmd.offsets[i] = 0; + size = mode_cmd.height * mode_cmd.pitches[i]; + exynos_gem[i] = exynos_drm_gem_create(dev, flags, size); + if (IS_ERR(exynos_gem[i])) { + ret = PTR_ERR(exynos_gem[i]); + goto err_free; + } + } + + fb = exynos_drm_framebuffer_init(dev, &mode_cmd, exynos_gem, num_planes); + if (IS_ERR(fb)) { + ret = PTR_ERR(fb); + goto err_free; + } + + return fb; + +err_free: + for (;i >= 0; i--) + exynos_drm_gem_destroy(exynos_gem[i]); + return ERR_PTR(ret); +} + +static bool exynos_plane_ipp_check_fb(struct drm_framebuffer *ipp_fb, + struct exynos_drm_plane_state *state) +{ + if (state->ipp_dst.x + state->ipp_dst.w <= ipp_fb->width && + state->ipp_dst.y + state->ipp_dst.h <= ipp_fb->height && + state->base.fb->pixel_format == ipp_fb->pixel_format) + return true; + return false; +} + +static int exynos_plane_ipp_transform(struct exynos_drm_plane_state *state) +{ + struct drm_framebuffer *src_fb = state->base.fb; + struct drm_framebuffer *dst_fb = state->ipp_fb; + struct drm_exynos_ipp_config src_config = { + .ops_id = EXYNOS_DRM_OPS_SRC, + .sz = { + .hsize = src_fb->pitches[0] / + drm_format_plane_cpp(src_fb->pixel_format, 0), + .vsize = src_fb->height, + }, + .fmt = src_fb->pixel_format, + .pos = { + .x = state->ipp_src.x, + .y = state->ipp_src.y, + .w = state->ipp_src.w, + .h = state->ipp_src.h, + }, + }; + struct drm_exynos_ipp_config dst_config = { + .sz = { + .hsize = dst_fb->pitches[0] / + drm_format_plane_cpp(dst_fb->pixel_format, 0), + .vsize = dst_fb->height, + }, + .fmt = dst_fb->pixel_format, + .pos = { + .x = state->ipp_dst.x, + .y = state->ipp_dst.y, + .w = state->ipp_dst.w, + .h = state->ipp_dst.h, + }, + }; + int degree = 0, flip = 0; + + if (state->rotation & BIT(DRM_ROTATE_180)) + degree = EXYNOS_DRM_DEGREE_180; + else if (state->rotation & BIT(DRM_ROTATE_90)) + degree = EXYNOS_DRM_DEGREE_90; + else if (state->rotation & BIT(DRM_ROTATE_270)) + degree = EXYNOS_DRM_DEGREE_270; + + if (state->rotation & BIT(DRM_REFLECT_X)) + flip |= EXYNOS_DRM_FLIP_HORIZONTAL; + if (state->rotation & BIT(DRM_REFLECT_Y)) + flip |= EXYNOS_DRM_FLIP_VERTICAL; + + dst_config.flip = flip; + dst_config.degree = degree; + + return exynos_ipp_process_internal(src_fb->dev, &src_config, src_fb, + &dst_config, dst_fb); +} + +void exynos_plane_ipp_setup(struct exynos_drm_plane_state *state, + const struct exynos_drm_plane_config *config, + unsigned int *src_x, unsigned int *src_y, + unsigned int *src_w, unsigned int *src_h, + unsigned int *crtc_w, unsigned int *crtc_h) +{ + int rotation = state->base.rotation; + int pre_x, pre_y, post_x, post_y; + int i; + bool supported_pixel_format = + exynos_plane_check_format(config, state->base.fb->pixel_format); + + state->rotation = rotation; + + /* check if ipp is really needed */ + if (rotation == BIT(DRM_ROTATE_0) && supported_pixel_format && + (*src_w == *crtc_w || + ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && + *src_w * 2 == *crtc_w) || + config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE) && + (*src_h == *crtc_h || + ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && + *src_h * 2 == *crtc_h) || + config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE)) + return; + + state->ipp_needed = true; + + state->ipp_pixel_format = state->base.fb->pixel_format; + if (!supported_pixel_format) { + for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) + if (exynos_plane_check_format(config, + preferred_formats[i])) { + state->ipp_pixel_format = preferred_formats[i]; + break; + } + } + + state->ipp_src.x = rounddown(*src_x, EXYNOS_DRM_PITCH_ALIGN); + state->ipp_src.y = rounddown(*src_y, EXYNOS_DRM_PITCH_ALIGN); + state->ipp_dst.x = 0; + state->ipp_dst.y = 0; + + pre_x = *src_x & (EXYNOS_DRM_PITCH_ALIGN - 1); + pre_y = *src_y & (EXYNOS_DRM_PITCH_ALIGN - 1); + + state->ipp_src.w = roundup(*src_w + pre_x, EXYNOS_DRM_PITCH_ALIGN); + state->ipp_src.h = roundup(*src_h + pre_y, EXYNOS_DRM_PITCH_ALIGN); + if (state->ipp_src.w > state->base.fb->pitches[0]) + state->ipp_src.w = state->base.fb->pitches[0]; + if (state->ipp_src.h > state->base.fb->height) + state->ipp_src.h = state->base.fb->height; + + state->ipp_dst.w = state->ipp_src.w; + state->ipp_dst.h = state->ipp_src.h; + + post_x = state->ipp_src.w - pre_x - *src_w; + post_y = state->ipp_src.h - pre_y - *src_h; + + if (rotation & BIT(DRM_REFLECT_Y)) + swap(pre_y, post_y); + + if (rotation & BIT(DRM_REFLECT_X)) + swap(pre_x, post_x); + + switch (rotation & 0xf) { + case BIT(DRM_ROTATE_0): + *src_x = pre_x; + *src_y = pre_y; + break; + case BIT(DRM_ROTATE_90): + *src_x = post_y; + *src_y = pre_x; + swap(*src_w, *src_h); + swap(state->ipp_dst.w, state->ipp_dst.h); + break; + case BIT(DRM_ROTATE_180): + *src_x = post_x; + *src_y = post_y; + break; + case BIT(DRM_ROTATE_270): + *src_x = pre_y; + *src_y = post_x; + swap(*src_w, *src_h); + swap(state->ipp_dst.w, state->ipp_dst.h); + break; + } + + /* apply scalling */ + state->ipp_dst.w = state->ipp_dst.w * *crtc_w / *src_w; + state->ipp_dst.h = state->ipp_dst.h * *crtc_h / *src_h; + + *src_x = *src_x * *crtc_w / *src_w; + *src_y = *src_y * *crtc_h / *src_h; + *src_w = *crtc_w; + *src_h = *crtc_h; +} + +int exynos_plane_ipp_check(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state) +{ + if (!state->ipp_needed) + return 0; + + /* check if currently allocated ipp fb can be reused */ + if (plane->ipp_next_fb && + !exynos_plane_ipp_check_fb(plane->ipp_next_fb, state)) { + drm_framebuffer_unreference(plane->ipp_next_fb); + plane->ipp_next_fb = NULL; + } + + /* allocate new ipp fb */ + if (!plane->ipp_next_fb) { + struct drm_framebuffer *ipp_fb; + + ipp_fb = exynos_plane_ipp_alloc_fb(plane->base.dev, state); + if (IS_ERR(ipp_fb)) + return PTR_ERR(ipp_fb); + plane->ipp_next_fb = ipp_fb; + } + + state->fb = state->ipp_fb = plane->ipp_next_fb; + + /* perform transformation */ + return exynos_plane_ipp_transform(state); +} + +void exynos_plane_ipp_update(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state) +{ + if (!state->ipp_needed) + return; + + if (plane->ipp_next_fb) + swap(plane->ipp_next_fb, plane->ipp_cur_fb); +} + +void exynos_plane_ipp_cleanup(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *old_state) +{ + struct exynos_drm_plane_state *state; + + /* get current plane state */ + state = to_exynos_plane_state(plane->base.state); + + if (!state->ipp_needed) { + if (plane->ipp_cur_fb) { + drm_framebuffer_unreference(plane->ipp_cur_fb); + plane->ipp_cur_fb = NULL; + } + if (plane->ipp_next_fb) { + drm_framebuffer_unreference(plane->ipp_next_fb); + plane->ipp_next_fb = NULL; + } + } +} + +int exynos_plane_ipp_init(struct drm_device *dev, + struct exynos_drm_plane *plane) +{ + int i, j, num_formats, ipp_formats = 0; + uint32_t *formats; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) + if (!exynos_plane_check_format(plane->config, + supported_formats[i])) + ipp_formats++; + + num_formats = plane->config->num_pixel_formats + ipp_formats; + formats = kmalloc_array(num_formats, sizeof(uint32_t), GFP_KERNEL); + if (!formats) + return -ENOMEM; + + memcpy(formats, plane->config->pixel_formats, + plane->config->num_pixel_formats * sizeof(uint32_t)); + j = plane->config->num_pixel_formats; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) + if (!exynos_plane_check_format(plane->config, + supported_formats[i])) + formats[j++] = supported_formats[i]; + + kfree(plane->base.format_types); + plane->base.format_types = formats; + plane->base.format_count = num_formats; + + if (!dev->mode_config.rotation_property) + dev->mode_config.rotation_property = + drm_mode_create_rotation_property(dev, + BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) | + BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) | + BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); + + if (dev->mode_config.rotation_property) + drm_object_attach_property(&plane->base.base, + dev->mode_config.rotation_property, + BIT(DRM_ROTATE_0)); + + return 0; +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.h new file mode 100644 index 000000000000..44b7f2c53829 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_plane_ipp.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _EXYNOS_DRM_PLANE_IPP_H_ +#define _EXYNOS_DRM_PLANE_IPP_H_ + +#ifdef CONFIG_DRM_EXYNOS_PLANE_IPP + +void exynos_plane_ipp_setup(struct exynos_drm_plane_state *state, + const struct exynos_drm_plane_config *config, + unsigned int *src_x, unsigned int *src_y, + unsigned int *src_w, unsigned int *src_h, + unsigned int *crtc_w, unsigned int *crtc_h); + +int exynos_plane_ipp_check(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state); + +void exynos_plane_ipp_update(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state); + +void exynos_plane_ipp_cleanup(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *old_state); + +int exynos_plane_ipp_init(struct drm_device *dev, + struct exynos_drm_plane *plane); + +#else + +static inline +void exynos_plane_ipp_setup(struct exynos_drm_plane_state *state, + const struct exynos_drm_plane_config *config, + unsigned int *src_x, unsigned int *src_y, + unsigned int *src_w, unsigned int *src_h, + unsigned int *crtc_w, unsigned int *crtc_h) +{ +} + +static inline +int exynos_plane_ipp_check(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state) +{ + return 0; +} + +static inline +void exynos_plane_ipp_update(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *state) +{ +} + +static inline +void exynos_plane_ipp_cleanup(struct exynos_drm_plane *plane, + struct exynos_drm_plane_state *old_state) +{ +} + +static inline +int exynos_plane_ipp_init(struct drm_device *dev, + struct exynos_drm_plane *plane) +{ + return 0; +} + +#endif +#endif