summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorFancy Fang <chen.fang@nxp.com>2018-06-06 22:52:35 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commitc30528f1567ca9cad24117e15f443ff45bcbc895 (patch)
tree7ce07b76983ed6f851d60663c0a2c250eb173e6c /drivers/gpu
parent2d1c4ecde1362e2a41cdf436c4cb87a1acc0036d (diff)
MLK-18535-5 drm/imx: add LCDIF DRM/KMS driver
This is a new DRM/KMS driver for LCDIF which conforms to the IMX DRM Core framework. It provides support for CRTCs, Planes and mode config of KMS. Signed-off-by: Fancy Fang <chen.fang@nxp.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/imx/Kconfig3
-rw-r--r--drivers/gpu/drm/imx/Makefile1
-rw-r--r--drivers/gpu/drm/imx/lcdif/Kconfig8
-rw-r--r--drivers/gpu/drm/imx/lcdif/Makefile4
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-crtc.c367
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-kms.c46
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-kms.h21
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-plane.c231
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-plane.h37
9 files changed, 717 insertions, 1 deletions
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index cee1a0b58cb1..b3105f444098 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -5,7 +5,7 @@ config DRM_IMX
select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER
depends on DRM
- depends on IMX_IPUV3_CORE || IMX_DPU_CORE || IMX_DCSS_CORE
+ depends on IMX_IPUV3_CORE || IMX_DPU_CORE || IMX_DCSS_CORE || IMX_LCDIF_CORE
help
enable i.MX graphics support
@@ -51,3 +51,4 @@ source "drivers/gpu/drm/imx/ipuv3/Kconfig"
source "drivers/gpu/drm/imx/dpu/Kconfig"
source "drivers/gpu/drm/imx/hdp/Kconfig"
source "drivers/gpu/drm/imx/dcss/Kconfig"
+source "drivers/gpu/drm/imx/lcdif/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index 1fc8a8b2125f..8fe7b576a56e 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3/
obj-$(CONFIG_DRM_IMX_DPU) += dpu/
obj-$(CONFIG_DRM_IMX_HDP) += hdp/
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
+obj-$(CONFIG_DRM_IMX_LCDIF) += lcdif/
diff --git a/drivers/gpu/drm/imx/lcdif/Kconfig b/drivers/gpu/drm/imx/lcdif/Kconfig
new file mode 100644
index 000000000000..4460ffacd1f7
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/Kconfig
@@ -0,0 +1,8 @@
+config DRM_IMX_LCDIF
+ tristate "i.MX LCDIF controller DRM driver"
+ depends on DRM_IMX
+ depends on IMX_LCDIF_CORE
+ default y if DRM_IMX=y
+ default m if DRM_IMX=m
+ help
+ enable i.MX LCDIF controller DRM driver under DRM_IMX.
diff --git a/drivers/gpu/drm/imx/lcdif/Makefile b/drivers/gpu/drm/imx/lcdif/Makefile
new file mode 100644
index 000000000000..fcdecff9e861
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/gpu/drm/imx
+
+imx-lcdif-crtc-objs := lcdif-crtc.o lcdif-plane.o lcdif-kms.o
+obj-$(CONFIG_DRM_IMX_LCDIF) += imx-lcdif-crtc.o
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c
new file mode 100644
index 000000000000..f14ca734923a
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <video/imx-lcdif.h>
+#include <video/videomode.h>
+
+#include "imx-drm.h"
+#include "lcdif-plane.h"
+#include "lcdif-kms.h"
+
+struct lcdif_crtc {
+ struct device *dev;
+
+ struct drm_crtc base;
+ struct imx_drm_crtc *imx_crtc;
+ struct lcdif_plane *plane[2];
+
+ int vbl_irq;
+ u32 pix_fmt; /* drm fourcc */
+};
+
+#define to_lcdif_crtc(crtc) container_of(crtc, struct lcdif_crtc, base)
+
+static void lcdif_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
+
+ imx_drm_remove_crtc(lcdif_crtc->imx_crtc);
+ lcdif_crtc->imx_crtc = NULL;
+}
+
+static void lcdif_crtc_reset(struct drm_crtc *crtc)
+{
+ struct imx_crtc_state *state;
+
+ if (crtc->state) {
+ __drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+ state = to_imx_crtc_state(crtc->state);
+ kfree(state);
+ crtc->state = NULL;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return;
+
+ crtc->state = &state->base;
+ crtc->state->crtc = crtc;
+}
+
+static struct drm_crtc_state *lcdif_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+ struct imx_crtc_state *state, *orig_state;
+
+ if (WARN_ON(!crtc->state))
+ return NULL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+ orig_state = to_imx_crtc_state(crtc->state);
+ state->bus_format = orig_state->bus_format;
+ state->bus_flags = orig_state->bus_flags;
+ state->di_hsync_pin = orig_state->di_hsync_pin;
+ state->di_vsync_pin = orig_state->di_vsync_pin;
+
+ return &state->base;
+}
+
+static void lcdif_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ __drm_atomic_helper_crtc_destroy_state(state);
+ kfree(to_imx_crtc_state(state));
+}
+
+static const struct drm_crtc_funcs lcdif_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = lcdif_crtc_destroy,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = lcdif_crtc_reset,
+ .atomic_duplicate_state = lcdif_crtc_duplicate_state,
+ .atomic_destroy_state = lcdif_crtc_destroy_state,
+};
+
+static int lcdif_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ return 0;
+}
+
+static void lcdif_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ drm_crtc_vblank_on(crtc);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ WARN_ON(drm_crtc_vblank_get(crtc));
+ drm_crtc_arm_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static void lcdif_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ /* LCDIF doesn't have command buffer */
+ return;
+}
+
+static void lcdif_crtc_enable(struct drm_crtc *crtc)
+{
+ struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state);
+ struct videomode vm;
+
+ drm_display_mode_to_videomode(mode, &vm);
+
+ if (imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_HIGH)
+ vm.flags |= DISPLAY_FLAGS_DE_HIGH;
+ else
+ vm.flags |= DISPLAY_FLAGS_DE_LOW;
+
+ if (imx_crtc_state->bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)
+ vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+ else
+ vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+
+ pm_runtime_get_sync(lcdif_crtc->dev->parent);
+
+ lcdif_set_mode(lcdif, &vm);
+
+ /* defer the lcdif controller enable to plane update,
+ * since until then the lcdif config is complete to
+ * enable the controller to run actually.
+ */
+}
+
+static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ drm_crtc_vblank_off(crtc);
+
+ lcdif_disable_controller(lcdif);
+
+ pm_runtime_put(lcdif_crtc->dev->parent);
+}
+
+static const struct drm_crtc_helper_funcs lcdif_helper_funcs = {
+ .atomic_check = lcdif_crtc_atomic_check,
+ .atomic_begin = lcdif_crtc_atomic_begin,
+ .atomic_flush = lcdif_crtc_atomic_flush,
+ .enable = lcdif_crtc_enable,
+ .atomic_disable = lcdif_crtc_atomic_disable,
+};
+
+static int lcdif_enable_vblank(struct drm_crtc *crtc)
+{
+ struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+
+ lcdif_vblank_irq_enable(lcdif);
+ enable_irq(lcdif_crtc->vbl_irq);
+
+ return 0;
+}
+
+static void lcdif_disable_vblank(struct drm_crtc *crtc)
+{
+ struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+
+ disable_irq_nosync(lcdif_crtc->vbl_irq);
+ lcdif_vblank_irq_disable(lcdif);
+}
+
+static const struct imx_drm_crtc_helper_funcs lcdif_crtc_helper_funcs = {
+ .enable_vblank = lcdif_enable_vblank,
+ .disable_vblank = lcdif_disable_vblank,
+ .crtc_funcs = &lcdif_crtc_funcs,
+ .crtc_helper_funcs = &lcdif_helper_funcs,
+};
+
+static irqreturn_t lcdif_crtc_vblank_irq_handler(int irq, void *dev_id)
+{
+ struct lcdif_crtc *lcdif_crtc = dev_id;
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+
+ drm_crtc_handle_vblank(&lcdif_crtc->base);
+
+ lcdif_vblank_irq_clear(lcdif);
+
+ return IRQ_HANDLED;
+}
+
+static int lcdif_crtc_init(struct lcdif_crtc *lcdif_crtc,
+ struct lcdif_client_platformdata *pdata,
+ struct drm_device *drm)
+{
+ int ret;
+ struct lcdif_plane *primary = lcdif_crtc->plane[0];
+ struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
+
+ /* Primary plane
+ * The 'possible_crtcs' of primary plane will be
+ * recalculated during the 'crtc' initialization
+ * later.
+ */
+ primary = lcdif_plane_init(drm, lcdif, 0, DRM_PLANE_TYPE_PRIMARY, 0);
+ if (IS_ERR(primary))
+ return PTR_ERR(primary);
+ lcdif_crtc->plane[0] = primary;
+
+ /* TODO: Overlay plane */
+
+ ret = imx_drm_add_crtc(drm, &lcdif_crtc->base,
+ &lcdif_crtc->imx_crtc, &primary->base,
+ &lcdif_crtc_helper_funcs, pdata->of_node);
+ if (ret) {
+ dev_err(lcdif_crtc->dev, "failed to init crtc\n");
+ goto primary_plane_deinit;
+ }
+
+ lcdif_crtc->vbl_irq = lcdif_vblank_irq_get(lcdif);
+ WARN_ON(lcdif_crtc->vbl_irq < 0);
+
+ ret = devm_request_irq(lcdif_crtc->dev, lcdif_crtc->vbl_irq,
+ lcdif_crtc_vblank_irq_handler, 0,
+ dev_name(lcdif_crtc->dev), lcdif_crtc);
+ if (ret) {
+ dev_err(lcdif_crtc->dev,
+ "vblank irq request failed: %d\n", ret);
+ goto primary_plane_deinit;
+ }
+
+ disable_irq(lcdif_crtc->vbl_irq);
+
+ return 0;
+
+primary_plane_deinit:
+ lcdif_plane_deinit(drm, primary);
+
+ return ret;
+}
+
+static int lcdif_crtc_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int ret;
+ struct drm_device *drm = data;
+ struct lcdif_crtc *lcdif_crtc;
+ struct lcdif_client_platformdata *pdata = dev->platform_data;
+
+ dev_dbg(dev, "%s: lcdif crtc bind begin\n", __func__);
+
+ lcdif_crtc = devm_kzalloc(dev, sizeof(*lcdif_crtc), GFP_KERNEL);
+ if (!lcdif_crtc)
+ return -ENOMEM;
+
+ lcdif_crtc->dev = dev;
+
+ ret = lcdif_crtc_init(lcdif_crtc, pdata, drm);
+ if (ret)
+ return ret;
+
+ if (!drm->mode_config.funcs)
+ drm->mode_config.funcs = &lcdif_drm_mode_config_funcs;
+
+ if (!drm->mode_config.helper_private)
+ drm->mode_config.helper_private = &lcdif_drm_mode_config_helpers;
+
+ dev_set_drvdata(dev, lcdif_crtc);
+
+ dev_dbg(dev, "%s: lcdif crtc bind end\n", __func__);
+
+ return 0;
+}
+
+static void lcdif_crtc_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = data;
+ struct lcdif_crtc *lcdif_crtc = dev_get_drvdata(dev);
+
+ if (lcdif_crtc->imx_crtc) {
+ imx_drm_remove_crtc(lcdif_crtc->imx_crtc);
+ lcdif_crtc->imx_crtc = NULL;
+ }
+
+ lcdif_plane_deinit(drm, lcdif_crtc->plane[0]);
+}
+
+static const struct component_ops lcdif_crtc_ops = {
+ .bind = lcdif_crtc_bind,
+ .unbind = lcdif_crtc_unbind,
+};
+
+static int lcdif_crtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ dev_dbg(&pdev->dev, "%s: lcdif crtc probe begin\n", __func__);
+
+ if (!dev->platform_data) {
+ dev_err(dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ return component_add(dev, &lcdif_crtc_ops);
+}
+
+static int lcdif_crtc_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &lcdif_crtc_ops);
+
+ return 0;
+}
+
+static struct platform_driver lcdif_crtc_driver = {
+ .probe = lcdif_crtc_probe,
+ .remove = lcdif_crtc_remove,
+ .driver = {
+ .name = "imx-lcdif-crtc",
+ },
+};
+module_platform_driver(lcdif_crtc_driver);
+
+MODULE_DESCRIPTION("NXP i.MX LCDIF DRM CRTC driver");
+MODULE_AUTHOR("Fancy Fang <chen.fang@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-kms.c b/drivers/gpu/drm/imx/lcdif/lcdif-kms.c
new file mode 100644
index 000000000000..f013bf0ce7f8
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-kms.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_cma_helper.h>
+
+static void lcdif_drm_atomic_commit_tail(struct drm_atomic_state *state)
+{
+ struct drm_device *dev = state->dev;
+
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+ drm_atomic_helper_commit_modeset_enables(dev, state);
+
+ drm_atomic_helper_commit_planes(dev, state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
+
+ drm_atomic_helper_commit_hw_done(state);
+
+ drm_atomic_helper_wait_for_vblanks(dev, state);
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+}
+
+const struct drm_mode_config_funcs lcdif_drm_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+struct drm_mode_config_helper_funcs lcdif_drm_mode_config_helpers = {
+ .atomic_commit_tail = lcdif_drm_atomic_commit_tail,
+};
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-kms.h b/drivers/gpu/drm/imx/lcdif/lcdif-kms.h
new file mode 100644
index 000000000000..fcf7d257c6b7
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-kms.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LCDIF_KMS_H
+#define __LCDIF_KMS_H
+
+extern const struct drm_mode_config_funcs lcdif_drm_mode_config_funcs;
+extern struct drm_mode_config_helper_funcs lcdif_drm_mode_config_helpers;
+
+#endif
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-plane.c b/drivers/gpu/drm/imx/lcdif/lcdif-plane.c
new file mode 100644
index 000000000000..418fd4b3f264
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-plane.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_rect.h>
+#include <video/imx-lcdif.h>
+
+#include "lcdif-plane.h"
+
+static uint32_t lcdif_pixel_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_BGR565,
+};
+
+static int lcdif_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *plane_state)
+{
+ int ret;
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_crtc_state *crtc_state;
+ struct drm_display_mode *mode;
+ struct drm_rect clip = { 0 };
+ unsigned int flags;
+
+ /* 'fb' should also be NULL which has been checked in
+ * the core sanity check function 'drm_atomic_plane_check()'
+ */
+ if (!plane_state->crtc) {
+ WARN_ON(fb);
+ return 0;
+ }
+
+ /* lcdif crtc can only display from (0,0) for each plane */
+ if (plane_state->crtc_x || plane_state->crtc_y)
+ return -EINVAL;
+
+ crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
+ plane_state->crtc);
+ mode = &crtc_state->adjusted_mode;
+
+ /* check fb pixel format matches bus format */
+ flags = mode->private_flags & 0xffff;
+
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_RGB565:
+ if (flags != MEDIA_BUS_FMT_RGB565_1X16)
+ return -EINVAL;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ if (flags != MEDIA_BUS_FMT_RGB888_1X24)
+ return -EINVAL;
+ break;
+ default:
+ /* TODO: add other formats support later */
+ return -EINVAL;
+ }
+
+ clip.x2 = mode->hdisplay;
+ clip.y2 = mode->vdisplay;
+
+ ret = drm_plane_helper_check_state(plane_state, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ false, true);
+ if (ret)
+ return ret;
+
+ if (!plane_state->visible)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void lcdif_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct lcdif_plane *lcdif_plane = to_lcdif_plane(plane);
+ struct lcdif_soc *lcdif = lcdif_plane->lcdif;
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_framebuffer *old_fb = old_state->fb;
+ struct drm_gem_cma_object *gem_obj = NULL;
+ u32 fb_addr, src_off, fb_idx;
+
+ /* plane and crtc is disabling */
+ if (!fb)
+ return;
+
+ /* TODO: for now we just update the next buf addr
+ * and the fb pixel format, since the mode set will
+ * be done in crtc's ->enable() helper func
+ */
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
+ (!old_fb || fb->pixel_format != old_fb->pixel_format))
+ lcdif_set_pix_fmt(lcdif, fb->pixel_format);
+
+ switch (plane->type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ /* TODO: only support RGB */
+ gem_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ src_off = (state->src_y >> 16) * fb->pitches[0] +
+ (state->src_x >> 16) * fb->bits_per_pixel;
+ fb_addr = gem_obj->paddr + fb->offsets[0] + src_off;
+ fb_idx = 0;
+ break;
+ default:
+ /* TODO: add overlay later */
+ return;
+ }
+
+ lcdif_set_fb_addr(lcdif, fb_idx, fb_addr);
+
+ lcdif_enable_controller(lcdif);
+}
+
+static void lcdif_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+
+ WARN_ON(fb);
+
+ /* TODO: CRTC disabled has been done by CRTC helper function,
+ * so it seems that no more required, the only possible thing
+ * is to set next buf addr to 0 in CRTC
+ */
+}
+
+static const struct drm_plane_helper_funcs lcdif_plane_helper_funcs = {
+ .atomic_check = lcdif_plane_atomic_check,
+ .atomic_update = lcdif_plane_atomic_update,
+ .atomic_disable = lcdif_plane_atomic_disable,
+};
+
+static void lcdif_plane_destroy(struct drm_plane *plane)
+{
+ struct lcdif_plane *lcdif_plane = to_lcdif_plane(plane);
+
+ drm_plane_cleanup(plane);
+ kfree(lcdif_plane);
+}
+
+static const struct drm_plane_funcs lcdif_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = lcdif_plane_destroy,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .set_property = drm_atomic_helper_plane_set_property,
+};
+
+struct lcdif_plane *lcdif_plane_init(struct drm_device *dev,
+ struct lcdif_soc *lcdif,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type,
+ unsigned int zpos)
+{
+ int ret;
+ struct lcdif_plane *lcdif_plane;
+
+ /* lcdif doesn't support fb modifiers */
+ if (zpos || dev->mode_config.allow_fb_modifiers)
+ return ERR_PTR(-EINVAL);
+
+ lcdif_plane = kzalloc(sizeof(*lcdif_plane), GFP_KERNEL);
+ if (!lcdif_plane)
+ return ERR_PTR(-ENOMEM);
+
+ lcdif_plane->lcdif = lcdif;
+
+ drm_plane_helper_add(&lcdif_plane->base, &lcdif_plane_helper_funcs);
+ ret = drm_universal_plane_init(dev, &lcdif_plane->base, possible_crtcs,
+ &lcdif_plane_funcs, lcdif_pixel_formats,
+ ARRAY_SIZE(lcdif_pixel_formats), NULL,
+ type, NULL);
+ if (ret) {
+ kfree(lcdif_plane);
+ return ERR_PTR(ret);
+ }
+
+ ret = drm_plane_create_zpos_immutable_property(&lcdif_plane->base, zpos);
+ if (ret) {
+ kfree(lcdif_plane);
+ return ERR_PTR(ret);
+ }
+
+ return lcdif_plane;
+}
+
+void lcdif_plane_deinit(struct drm_device *dev,
+ struct lcdif_plane *lcdif_plane)
+{
+ struct drm_plane *plane = &lcdif_plane->base;
+
+ if (plane->zpos_property)
+ drm_property_destroy(dev, plane->zpos_property);
+
+ lcdif_plane_destroy(plane);
+}
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-plane.h b/drivers/gpu/drm/imx/lcdif/lcdif-plane.h
new file mode 100644
index 000000000000..acd7aead606a
--- /dev/null
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-plane.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LCDIF_PLANE_H
+#define __LCDIF_PLANE_H
+
+#include <drm/drm_plane.h>
+#include <video/imx-lcdif.h>
+
+struct lcdif_plane {
+ struct drm_plane base;
+ struct lcdif_soc *lcdif;
+};
+
+#define to_lcdif_plane(plane) container_of(plane, struct lcdif_plane, base)
+
+struct lcdif_plane *lcdif_plane_init(struct drm_device *drm,
+ struct lcdif_soc *lcdif,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type,
+ unsigned int zpos);
+
+void lcdif_plane_deinit(struct drm_device *dev,
+ struct lcdif_plane *lcdif_plane);
+
+#endif