@@ -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
@@ -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
@@ -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)
@@ -20,6 +20,7 @@
#include <drm/drmP.h>
#include <drm/exynos_drm.h>
#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;
@@ -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,
@@ -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);
}
new file mode 100644
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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 <drm/drmP.h>
+
+#include <drm/exynos_drm.h>
+#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;
+}
new file mode 100644
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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
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 <m.szyprowski@samsung.com> --- 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