MLK-18535-5 drm/imx: add LCDIF DRM/KMS driver
authorFancy Fang <chen.fang@nxp.com>
Wed, 6 Jun 2018 14:52:35 +0000 (22:52 +0800)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
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>
(cherry picked from commit a104e928da7281a3089ff31afb2df979f35716b8)

drivers/gpu/drm/imx/Kconfig
drivers/gpu/drm/imx/Makefile
drivers/gpu/drm/imx/lcdif/Kconfig [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/Makefile [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/lcdif-crtc.c [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/lcdif-kms.c [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/lcdif-kms.h [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/lcdif-plane.c [new file with mode: 0644]
drivers/gpu/drm/imx/lcdif/lcdif-plane.h [new file with mode: 0644]
drivers/gpu/imx/lcdif/Kconfig

index 3d1e7d7..29fcd2d 100644 (file)
@@ -5,6 +5,7 @@ config DRM_IMX
        select DRM_GEM_CMA_HELPER
        select DRM_KMS_CMA_HELPER
        depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM)
+       depends on IMX_LCDIF_CORE
        help
          enable i.MX graphics support
 
@@ -52,3 +53,5 @@ config DRM_IMX_NWL_DSI
        help
          Choose this to enable the internal NWL MIPI DSI controller
          found on i.MX8 processors.
+
+source "drivers/gpu/drm/imx/lcdif/Kconfig"
index 6af9054..e0e1749 100644 (file)
@@ -12,3 +12,4 @@ imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o ipuv3-kms.o
 obj-$(CONFIG_DRM_IMX_IPUV3)    += imx-ipuv3-crtc.o
 obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
 obj-$(CONFIG_DRM_IMX_NWL_DSI) += nwl_dsi-imx.o
+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 (file)
index 0000000..4460ffa
--- /dev/null
@@ -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 (file)
index 0000000..fcdecff
--- /dev/null
@@ -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 (file)
index 0000000..0d76d90
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * 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 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_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 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_atomic_enable(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);
+       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,
+       .atomic_enable  = lcdif_crtc_atomic_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 drm_crtc_funcs lcdif_crtc_funcs = {
+       .set_config = drm_atomic_helper_set_config,
+       .destroy    = drm_crtc_cleanup,
+       .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,
+       .enable_vblank  = lcdif_enable_vblank,
+       .disable_vblank = lcdif_disable_vblank,
+};
+
+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 */
+
+       lcdif_crtc->base.port = pdata->of_node;
+       drm_crtc_helper_add(&lcdif_crtc->base, &lcdif_helper_funcs);
+       ret = drm_crtc_init_with_planes(drm, &lcdif_crtc->base,
+                       &lcdif_crtc->plane[0]->base, NULL,
+                       &lcdif_crtc_funcs, NULL);
+       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);
+
+       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 (file)
index 0000000..99d1f86
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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>
+#include <drm/drm_gem_framebuffer_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_gem_fb_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 (file)
index 0000000..fcf7d25
--- /dev/null
@@ -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 (file)
index 0000000..bf8e949
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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;
+       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->format->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;
+       }
+
+       ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+                                                 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->format->format != old_fb->format->format))
+               lcdif_set_pix_fmt(lcdif, fb->format->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->format->cpp[0];
+               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,
+};
+
+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 (file)
index 0000000..acd7aea
--- /dev/null
@@ -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
index 326cebd..5736a90 100644 (file)
@@ -1,7 +1,7 @@
 config IMX_LCDIF_CORE
        tristate "i.MX LCDIF core support"
        depends on ARCH_FSL_IMX8MM
-       depends on RESET_CONTROLLER
+       select RESET_CONTROLLER
        help
          Choose this if you have a NXP i.MX8MM platform and want to use the
          LCDIF display controller. This option only enables LCDIF base support.