MLK-16675-2: drm: imx: add mscale DCSS drm driver
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>
Thu, 19 Oct 2017 09:29:54 +0000 (12:29 +0300)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
This patch adds DRM KMS support for i.MX8M's DCSS.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
drivers/gpu/drm/imx/Kconfig
drivers/gpu/drm/imx/Makefile
drivers/gpu/drm/imx/dcss/Kconfig [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/Makefile [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/dcss-crtc.c [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/dcss-kms.c [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/dcss-kms.h [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/dcss-plane.c [new file with mode: 0644]
drivers/gpu/drm/imx/dcss/dcss-plane.h [new file with mode: 0644]

index 8a7f1aa..5b960d8 100644 (file)
@@ -5,7 +5,6 @@ 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
 
@@ -64,3 +63,4 @@ config DRM_IMX_SEC_DSIM
           found on i.MX platform.
 
 source "drivers/gpu/drm/imx/lcdif/Kconfig"
+source "drivers/gpu/drm/imx/dcss/Kconfig"
index f0af866..26f4089 100644 (file)
@@ -14,3 +14,4 @@ obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
 obj-$(CONFIG_DRM_IMX_NWL_DSI) += nwl_dsi-imx.o
 obj-$(CONFIG_DRM_IMX_SEC_DSIM) += sec_mipi_dsim-imx.o
 obj-$(CONFIG_DRM_IMX_LCDIF) += lcdif/
+obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig
new file mode 100644 (file)
index 0000000..f958155
--- /dev/null
@@ -0,0 +1,6 @@
+config DRM_IMX_DCSS
+       tristate
+       depends on DRM_IMX
+       depends on IMX_DCSS_CORE
+       default y if DRM_IMX=y
+       default m if DRM_IMX=m
diff --git a/drivers/gpu/drm/imx/dcss/Makefile b/drivers/gpu/drm/imx/dcss/Makefile
new file mode 100644 (file)
index 0000000..f7f7e9e
--- /dev/null
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/gpu/drm/imx
+
+imx-dcss-crtc-objs := dcss-crtc.o dcss-plane.o dcss-kms.o
+obj-$(CONFIG_DRM_IMX_DCSS) += imx-dcss-crtc.o
diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/imx/dcss/dcss-crtc.c
new file mode 100644 (file)
index 0000000..064acf6
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2017 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 <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/component.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <video/imx-dcss.h>
+
+#include "dcss-kms.h"
+#include "dcss-plane.h"
+#include "imx-drm.h"
+
+struct dcss_crtc {
+       struct device *dev;
+       struct drm_crtc         base;
+
+       struct dcss_plane       *plane[3];
+
+       int                     irq;
+
+       struct drm_property *alpha;
+};
+
+static void dcss_crtc_reset(struct drm_crtc *crtc)
+{
+       struct imx_crtc_state *state;
+
+       if (crtc->state) {
+               if (crtc->state->mode_blob)
+                       drm_property_blob_put(crtc->state->mode_blob);
+
+               state = to_imx_crtc_state(crtc->state);
+               memset(state, 0, sizeof(*state));
+       } else {
+               state = kzalloc(sizeof(*state), GFP_KERNEL);
+               if (!state)
+                       return;
+               crtc->state = &state->base;
+       }
+
+       state->base.crtc = crtc;
+}
+
+static struct drm_crtc_state *dcss_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+       struct imx_crtc_state *state;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+       WARN_ON(state->base.crtc != crtc);
+       state->base.crtc = crtc;
+
+       return &state->base;
+}
+
+static void dcss_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 dcss_enable_vblank(struct drm_crtc *crtc)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       dcss_vblank_irq_enable(dcss, true);
+
+       enable_irq(dcss_crtc->irq);
+
+       return 0;
+}
+
+static void dcss_disable_vblank(struct drm_crtc *crtc)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       disable_irq_nosync(dcss_crtc->irq);
+
+       dcss_vblank_irq_enable(dcss, false);
+}
+
+static const struct drm_crtc_funcs dcss_crtc_funcs = {
+       .set_config = drm_atomic_helper_set_config,
+       .destroy = drm_crtc_cleanup,
+       .page_flip = drm_atomic_helper_page_flip,
+       .reset = dcss_crtc_reset,
+       .atomic_duplicate_state = dcss_crtc_duplicate_state,
+       .atomic_destroy_state = dcss_crtc_destroy_state,
+       .enable_vblank = dcss_enable_vblank,
+       .disable_vblank = dcss_disable_vblank,
+};
+
+static void dcss_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+       struct videomode vm;
+
+       drm_display_mode_to_videomode(mode, &vm);
+
+       dcss_dtg_sync_set(dcss, &vm);
+       dcss_ss_sync_set(dcss, &vm, mode->flags & DRM_MODE_FLAG_PHSYNC,
+                        mode->flags & DRM_MODE_FLAG_PVSYNC);
+}
+
+static int dcss_crtc_atomic_check(struct drm_crtc *crtc,
+                                 struct drm_crtc_state *state)
+{
+       /* TODO: other checks? */
+
+       return 0;
+}
+
+static void dcss_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 dcss_crtc_atomic_flush(struct drm_crtc *crtc,
+                                  struct drm_crtc_state *old_crtc_state)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       if (dcss_dtg_is_enabled(dcss))
+               dcss_ctxld_enable(dcss);
+}
+
+static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
+                                   struct drm_crtc_state *old_crtc_state)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       dcss_ss_enable(dcss, true);
+       dcss_dtg_enable(dcss, true);
+       dcss_ctxld_enable(dcss);
+}
+
+static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *old_crtc_state)
+{
+       struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+                                                  base);
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
+
+       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);
+
+       dcss_ss_enable(dcss, false);
+       dcss_dtg_enable(dcss, false);
+       dcss_ctxld_enable(dcss);
+}
+
+static const struct drm_crtc_helper_funcs dcss_helper_funcs = {
+       .mode_set_nofb = dcss_crtc_mode_set_nofb,
+       .atomic_check = dcss_crtc_atomic_check,
+       .atomic_begin = dcss_crtc_atomic_begin,
+       .atomic_flush = dcss_crtc_atomic_flush,
+       .atomic_enable = dcss_crtc_atomic_enable,
+       .atomic_disable = dcss_crtc_atomic_disable,
+};
+
+static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)
+{
+       struct dcss_crtc *dcss_crtc = dev_id;
+       struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
+
+       drm_crtc_handle_vblank(&dcss_crtc->base);
+
+       dcss_vblank_irq_clear(dcss);
+
+       return IRQ_HANDLED;
+}
+
+static int dcss_crtc_init(struct dcss_crtc *crtc,
+                         struct dcss_client_platformdata *pdata,
+                         struct drm_device *drm)
+{
+       struct dcss_soc *dcss = dev_get_drvdata(crtc->dev->parent);
+       int ret;
+
+       crtc->plane[0] = dcss_plane_init(drm, dcss, drm_crtc_mask(&crtc->base),
+                                        DRM_PLANE_TYPE_PRIMARY, 2);
+       if (IS_ERR(crtc->plane[0]))
+               return PTR_ERR(crtc->plane[0]);
+
+       drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);
+       ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,
+                                       NULL, &dcss_crtc_funcs, NULL);
+       if (ret) {
+               dev_err(crtc->dev, "failed to init crtc\n");
+               return ret;
+       }
+
+       crtc->plane[1] = dcss_plane_init(drm, dcss, drm_crtc_mask(&crtc->base),
+                                        DRM_PLANE_TYPE_OVERLAY, 1);
+       if (IS_ERR(crtc->plane[1]))
+               crtc->plane[1] = NULL;
+
+       crtc->plane[2] = dcss_plane_init(drm, dcss, drm_crtc_mask(&crtc->base),
+                                        DRM_PLANE_TYPE_OVERLAY, 0);
+       if (IS_ERR(crtc->plane[2]))
+               crtc->plane[2] = NULL;
+
+       crtc->alpha = drm_property_create_range(drm, 0, "alpha", 0, 255);
+       if (!crtc->alpha) {
+               dev_err(crtc->dev, "cannot create alpha property\n");
+               return -ENOMEM;
+       }
+
+       /* attach alpha property to channel 0 */
+       drm_object_attach_property(&crtc->plane[0]->base.base,
+                                  crtc->alpha, 255);
+       crtc->plane[0]->alpha_prop = crtc->alpha;
+
+       crtc->irq = dcss_vblank_irq_get(dcss);
+       if (crtc->irq < 0) {
+               dev_err(crtc->dev, "unable to get vblank interrupt\n");
+               return crtc->irq;
+       }
+
+       ret = devm_request_irq(crtc->dev, crtc->irq, dcss_crtc_irq_handler,
+                              IRQF_TRIGGER_RISING, "dcss_drm", crtc);
+       if (ret) {
+               dev_err(crtc->dev, "irq request failed with %d.\n", ret);
+               return ret;
+       }
+
+       drm->mode_config.allow_fb_modifiers = true;
+
+       disable_irq(crtc->irq);
+
+       return 0;
+}
+
+static int dcss_crtc_bind(struct device *dev, struct device *master,
+                         void *data)
+{
+       struct dcss_client_platformdata *pdata = dev->platform_data;
+       struct drm_device *drm = data;
+       struct dcss_crtc *crtc;
+       int ret;
+
+       crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+       if (!crtc)
+               return -ENOMEM;
+
+       crtc->dev = dev;
+
+       ret = dcss_crtc_init(crtc, pdata, drm);
+       if (ret)
+               return ret;
+
+       if (!drm->mode_config.funcs)
+               drm->mode_config.funcs = &dcss_drm_mode_config_funcs;
+
+       dev_set_drvdata(dev, crtc);
+
+       return 0;
+}
+
+static void dcss_crtc_unbind(struct device *dev, struct device *master,
+                            void *data)
+{
+}
+
+static const struct component_ops dcss_crtc_ops = {
+       .bind = dcss_crtc_bind,
+       .unbind = dcss_crtc_unbind,
+};
+
+static int dcss_crtc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       if (!dev->platform_data) {
+               dev_err(dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       return component_add(dev, &dcss_crtc_ops);
+}
+
+static int dcss_crtc_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dcss_crtc_ops);
+       return 0;
+}
+
+static struct platform_driver dcss_crtc_driver = {
+       .driver = {
+               .name = "imx-dcss-crtc",
+       },
+       .probe = dcss_crtc_probe,
+       .remove = dcss_crtc_remove,
+};
+module_platform_driver(dcss_crtc_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>");
+MODULE_DESCRIPTION("i.MX DCSS CRTC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-dcss-crtc");
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c
new file mode 100644 (file)
index 0000000..65567d1
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 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_crtc.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <linux/dma-buf.h>
+#include <linux/reservation.h>
+
+#include "imx-drm.h"
+
+static void dcss_drm_output_poll_changed(struct drm_device *drm)
+{
+       struct imx_drm_device *imxdrm = drm->dev_private;
+
+       drm_fbdev_cma_hotplug_event(imxdrm->fbhelper);
+}
+
+static int dcss_drm_atomic_check(struct drm_device *drm,
+                                struct drm_atomic_state *state)
+{
+       int ret;
+
+       ret = drm_atomic_helper_check_modeset(drm, state);
+       if (ret)
+               return ret;
+
+       ret = drm_atomic_helper_check_planes(drm, state);
+       if (ret)
+               return ret;
+
+       /*
+        * Check modeset again in case crtc_state->mode_changed is
+        * updated in plane's ->atomic_check callback.
+        */
+       return drm_atomic_helper_check_modeset(drm, state);
+}
+
+static int dcss_drm_atomic_commit(struct drm_device *drm,
+                                 struct drm_atomic_state *state,
+                                 bool nonblock)
+{
+       struct drm_plane_state *plane_state;
+       struct drm_plane *plane;
+       struct dma_buf *dma_buf;
+       int i;
+
+       /*
+        * If the plane fb has an dma-buf attached, fish out the exclusive
+        * fence for the atomic helper to wait on.
+        */
+       for_each_new_plane_in_state(state, plane, plane_state, i) {
+               if ((plane->state->fb != plane_state->fb) && plane_state->fb) {
+                       dma_buf = drm_fb_cma_get_gem_obj(plane_state->fb,
+                                                        0)->base.dma_buf;
+                       if (!dma_buf)
+                               continue;
+                       plane_state->fence =
+                               reservation_object_get_excl_rcu(dma_buf->resv);
+               }
+       }
+
+       return drm_atomic_helper_commit(drm, state, nonblock);
+}
+
+const struct drm_mode_config_funcs dcss_drm_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create,
+       .output_poll_changed = dcss_drm_output_poll_changed,
+       .atomic_check = dcss_drm_atomic_check,
+       .atomic_commit = dcss_drm_atomic_commit,
+};
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h
new file mode 100644 (file)
index 0000000..3c53952
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 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 _DCSS_KMS_H_
+#define _DCSS_KMS_H_
+
+extern const struct drm_mode_config_funcs dcss_drm_mode_config_funcs;
+
+#endif
diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c
new file mode 100644 (file)
index 0000000..53c88a9
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2017 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_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_atomic.h>
+
+#include "video/imx-dcss.h"
+#include "dcss-plane.h"
+
+static const u32 dcss_common_formats[] = {
+       /* RGB */
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_RGBA8888,
+       DRM_FORMAT_RGBX8888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_RGBX1010102,
+       DRM_FORMAT_BGRX1010102,
+       DRM_FORMAT_ARGB2101010,
+       DRM_FORMAT_ABGR2101010,
+       DRM_FORMAT_RGBA1010102,
+       DRM_FORMAT_BGRA1010102,
+
+       /* YUV444 */
+       DRM_FORMAT_AYUV,
+
+       /* YUV422 */
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+
+       /* YUV420 */
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+};
+
+static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p)
+{
+       return container_of(p, struct dcss_plane, base);
+}
+
+static void dcss_plane_destroy(struct drm_plane *plane)
+{
+       struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane,
+                                                    base);
+
+       DRM_DEBUG_KMS("destroy plane\n");
+
+       drm_plane_cleanup(plane);
+       kfree(dcss_plane);
+}
+
+static int dcss_plane_atomic_set_property(struct drm_plane *plane,
+                                         struct drm_plane_state *state,
+                                         struct drm_property *property,
+                                         uint64_t val)
+{
+       struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+
+       if (property == dcss_plane->alpha_prop)
+               dcss_plane->alpha_val = val;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int dcss_plane_atomic_get_property(struct drm_plane *plane,
+                                         const struct drm_plane_state *state,
+                                         struct drm_property *property,
+                                         uint64_t *val)
+{
+       struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+
+       if (property == dcss_plane->alpha_prop)
+               *val = dcss_plane->alpha_val;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct drm_plane_funcs dcss_plane_funcs = {
+       .update_plane   = drm_atomic_helper_update_plane,
+       .disable_plane  = drm_atomic_helper_disable_plane,
+       .destroy        = dcss_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,
+       .atomic_set_property = dcss_plane_atomic_set_property,
+       .atomic_get_property = dcss_plane_atomic_get_property,
+};
+
+static int dcss_plane_atomic_check(struct drm_plane *plane,
+                                  struct drm_plane_state *state)
+{
+       struct drm_framebuffer *fb = state->fb;
+       struct drm_gem_cma_object *cma_obj;
+
+       if (!fb)
+               return 0;
+
+       if (!state->crtc)
+               return -EINVAL;
+
+       cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+       WARN_ON(!cma_obj);
+
+       /*
+        * TODO: more checks are probably needed here... I'll figure them
+        * out after I start testing.
+        */
+
+       return 0;
+}
+
+static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane)
+{
+       struct drm_plane *plane = &dcss_plane->base;
+       struct drm_plane_state *state = plane->state;
+       struct drm_framebuffer *fb = state->fb;
+       struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+       unsigned long p1_ba, p2_ba;
+       bool modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS);
+       u32 pix_format = state->fb->format->format;
+
+       BUG_ON(!cma_obj);
+
+       p1_ba = cma_obj->paddr + fb->offsets[0] +
+               fb->pitches[0] * (state->src_y >> 16) +
+               fb->format->cpp[0] * (state->src_x >> 16);
+
+       p2_ba = cma_obj->paddr + fb->offsets[1] +
+               fb->pitches[1] * (state->src_y >> 16) +
+               fb->format->cpp[0] * (state->src_x >> 16);
+
+       dcss_dpr_addr_set(dcss_plane->dcss, dcss_plane->ch_num,
+                         p1_ba, p2_ba, fb->pitches[0]);
+
+       if (!modifiers_present || (pix_format != DRM_FORMAT_NV12 &&
+                                  pix_format != DRM_FORMAT_NV21)) {
+               dcss_dtrc_bypass(dcss_plane->dcss, dcss_plane->ch_num);
+               return;
+       }
+
+       dcss_dtrc_addr_set(dcss_plane->dcss, dcss_plane->ch_num, p1_ba, p2_ba);
+}
+
+static bool dcss_plane_needs_setup(struct drm_plane_state *state,
+                                  struct drm_plane_state *old_state)
+{
+       return state->crtc_x != old_state->crtc_x ||
+              state->crtc_y != old_state->crtc_y ||
+              state->crtc_w != old_state->crtc_w ||
+              state->crtc_h != old_state->crtc_h ||
+              state->src_x  != old_state->src_x  ||
+              state->src_y  != old_state->src_y  ||
+              state->src_w  != old_state->src_w  ||
+              state->src_h  != old_state->src_h  ||
+              state->fb->format->format != old_state->fb->format->format;
+}
+
+static void dcss_plane_atomic_update(struct drm_plane *plane,
+                                    struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = plane->state;
+       struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+       u32 pixel_format = state->fb->format->format;
+       struct drm_crtc_state *crtc_state = state->crtc->state;
+
+       if (!state->fb)
+               return;
+
+       if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) &&
+           !dcss_plane_needs_setup(state, old_state) &&
+           !dcss_dtg_global_alpha_changed(dcss_plane->dcss, dcss_plane->ch_num,
+                                          pixel_format,
+                                          dcss_plane->alpha_val)) {
+               dcss_plane_atomic_set_base(dcss_plane);
+               return;
+       }
+
+       dcss_dtrc_set_res(dcss_plane->dcss, dcss_plane->ch_num,
+                         state->src_w >> 16, state->src_h >> 16);
+
+       dcss_dpr_format_set(dcss_plane->dcss, dcss_plane->ch_num, pixel_format);
+       dcss_dpr_set_res(dcss_plane->dcss, dcss_plane->ch_num,
+                        state->src_w >> 16, state->src_h >> 16);
+       dcss_plane_atomic_set_base(dcss_plane);
+
+       dcss_scaler_setup(dcss_plane->dcss, dcss_plane->ch_num,
+                         pixel_format, state->src_w >> 16,
+                         state->src_h >> 16, state->crtc_w, state->crtc_h);
+
+       /*
+        * TODO: retrieve the output colorspace format from somewhere... For
+        * now, assume RGB.
+        */
+       dcss_hdr10_pipe_csc_setup(dcss_plane->dcss, dcss_plane->ch_num,
+                                 dcss_drm_fourcc_to_colorspace(pixel_format),
+                                 DCSS_COLORSPACE_RGB);
+
+       dcss_dtg_plane_pos_set(dcss_plane->dcss, dcss_plane->ch_num,
+                              state->crtc_x, state->crtc_y,
+                              state->crtc_w, state->crtc_h);
+       dcss_dtg_plane_alpha_set(dcss_plane->dcss, dcss_plane->ch_num,
+                                pixel_format, dcss_plane->alpha_val);
+
+       dcss_dpr_enable(dcss_plane->dcss, dcss_plane->ch_num, true);
+       dcss_scaler_enable(dcss_plane->dcss, dcss_plane->ch_num, true);
+       dcss_dtg_ch_enable(dcss_plane->dcss, dcss_plane->ch_num, true);
+}
+
+static void dcss_plane_atomic_disable(struct drm_plane *plane,
+                                     struct drm_plane_state *old_state)
+{
+       struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+
+       dcss_dpr_enable(dcss_plane->dcss, dcss_plane->ch_num, false);
+       dcss_scaler_enable(dcss_plane->dcss, dcss_plane->ch_num, false);
+       dcss_dtg_plane_pos_set(dcss_plane->dcss, dcss_plane->ch_num,
+                              0, 0, 0, 0);
+       dcss_dtg_ch_enable(dcss_plane->dcss, dcss_plane->ch_num, false);
+}
+
+static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = {
+       .atomic_check = dcss_plane_atomic_check,
+       .atomic_update = dcss_plane_atomic_update,
+       .atomic_disable = dcss_plane_atomic_disable,
+};
+
+struct dcss_plane *dcss_plane_init(struct drm_device *drm,
+                                  struct dcss_soc *dcss,
+                                  unsigned int possible_crtcs,
+                                  enum drm_plane_type type,
+                                  unsigned int zpos)
+{
+       struct dcss_plane *dcss_plane;
+       int ret;
+
+       if (zpos > 2)
+               return ERR_PTR(-EINVAL);
+
+       dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL);
+       if (!dcss_plane) {
+               DRM_ERROR("failed to allocate plane\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dcss_plane->dcss = dcss;
+
+       ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs,
+                                      &dcss_plane_funcs, dcss_common_formats,
+                                      ARRAY_SIZE(dcss_common_formats),
+                                      NULL, type, NULL);
+       if (ret) {
+               DRM_ERROR("failed to initialize plane\n");
+               kfree(dcss_plane);
+               return ERR_PTR(ret);
+       }
+
+       drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs);
+
+       ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos);
+       if (ret)
+               return ERR_PTR(ret);
+
+       dcss_plane->ch_num = 2 - zpos;
+       dcss_plane->alpha_val = 255;
+
+       return dcss_plane;
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.h b/drivers/gpu/drm/imx/dcss/dcss-plane.h
new file mode 100644 (file)
index 0000000..56e51d9
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __DCSS_PLANE_H__
+#define __DCSS_PLANE_H__
+
+#include <drm/drm_crtc.h>
+
+struct dcss_plane {
+       struct drm_plane base;
+       struct dcss_soc *dcss;
+
+       int alpha_val;
+       struct drm_property *alpha_prop;
+
+       int ch_num;
+};
+
+struct dcss_plane *dcss_plane_init(struct drm_device *drm,
+                                  struct dcss_soc *dcss,
+                                  unsigned int possible_crtcs,
+                                  enum drm_plane_type type,
+                                  unsigned int zpos);
+
+#endif /* __DCSS_PLANE_H__ */