This patch adds DRM KMS support for i.MX8M's DCSS.
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
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
found on i.MX platform.
source "drivers/gpu/drm/imx/lcdif/Kconfig"
+source "drivers/gpu/drm/imx/dcss/Kconfig"
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/
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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__ */