OSDN Git Service

drm/msm: Early DRM Driver
authorCamus Wong <camusw@codeaurora.org>
Fri, 26 Oct 2018 06:47:37 +0000 (02:47 -0400)
committerCamus Wong <camusw@codeaurora.org>
Mon, 21 Jan 2019 16:53:41 +0000 (11:53 -0500)
Add new DRM node to handle early display service. The early DRM
driver is to workaround Andriod display framework long boot time
problem and DRM single master limitation.  The early DRM node provides
display function for early application that is outside Android
framework.  The early application can use early DRM to draw early
UI and bootloader review camera menu function. Android framework will
not use early DRM node.  It will continue to use the main DRM node.

Early DRM is another KMS driver that designed for bootup application.
Early DRM is not based on SDE framework and it will not initialize
display hardware.  Early DRM rely on bootloader to initialize display
hardware and interfaces.  For power and SMMU, early DRM relies on main
DRM to initialize them.  Early DRM only provide limited display
functionality such as RGB buffer display.  Early DRM only expected to
run during bootup time to work with bootloader/early RVC.  When Android
UI is ready, early DRM will handoff all display resource to main DRM.
After that, no application can open early DRM node.

Early DRM is enabled in device tree.  User must specify which display
to enable early DRM and which display pipes are assigned to eDRM.

Change-Id: Ic9f68726677c5db26507caec79c7da1e6d745f44
Signed-off-by: Camus Wong <camusw@codeaurora.org>
21 files changed:
Documentation/devicetree/bindings/display/msm/edrm.txt [new file with mode: 0644]
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/ekms/edrm_connector.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_connector.h [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_crtc.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_crtc.h [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_drv.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_encoder.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_encoder.h [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_kms.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_kms.h [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_plane.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_plane.h [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_splash.c [new file with mode: 0644]
drivers/gpu/drm/msm/ekms/edrm_splash.h [new file with mode: 0644]
drivers/gpu/drm/msm/sde/sde_hw_ctl.c
drivers/gpu/drm/msm/sde/sde_hw_ctl.h
drivers/gpu/drm/msm/sde/sde_kms.c
drivers/gpu/drm/msm/sde/sde_splash.c
drivers/gpu/drm/msm/sde/sde_splash.h

diff --git a/Documentation/devicetree/bindings/display/msm/edrm.txt b/Documentation/devicetree/bindings/display/msm/edrm.txt
new file mode 100644 (file)
index 0000000..f4856e8
--- /dev/null
@@ -0,0 +1,57 @@
+Qualcomm Technologies, Inc. EDRM KMS
+
+EDRM KMS implements Linux DRM/KMS APIs to drive early user interface to
+different panel interfaces. EDRM driver provides early service to display
+subsystem which manage a subset data paths to specific panel interfaces.
+EDRM is expected to provide display service to fill in the gap between
+bootloader and main application UI.  Only early application is expected to
+use EDRM.  EDRM will be terminated when main application UI is ready.
+
+Required properties
+- compatible: Must be "qcom,msm-kms-edrm"
+
+Assigned Display Subnodes:
+- qcom,edrm-assigned-display:  List of display that eDRM can use.
+
+Subnode properties:
+- qcom,edrm-display-id@x:      a node that contains the display information
+
+- qcom,label:  The string that indicate the label of the display.  The
+                       label should match with the label used in sde_display.
+                       For example, SDE display dtsi may defined:
+                               dsi_adv_7533_2: qcom,dsi-display@8 {...}
+
+                       If eDRM wants to use that display, it should set the
+                       label as:
+                               qcom,label = "dsi_adv_7533_1";
+
+- qcom,intf-type:  a string that indicate the interface type.  Right now
+                               it can be "dsi" or "hdmi"
+
+- qcom,assigned_plane :        List of plane that assigned for this display.
+                               There must be at least one plane in this field.
+                               The plane that is in this field must also be
+                               defined in qcom,sde-reserved-plane under sde_kms
+                               For example, SDE may contains the following
+                               reserved plane:
+                               &sde_kms {
+                                       qcom,sde-reserved-plane {
+                                       reserved_plane1: qcom,sde-reserved-plane@1 {
+                                       reg = <0x1>;
+                                       qcom,plane-name = "rgb3";
+                                       lm-stage = <5>;
+                               }
+                               Then assigned_plane field can reference to the
+                               reserved plane like:
+                               qcom,assigned_plane = <&reserved_plane1>;
+
+Example:
+       msm_kms_edrm: qcom,msm_kms_edrm@900000 {
+               qcom,edrm-assigned-display {
+                       qcom,edrm-display-id@0 {
+                               qcom,label = "dsi_adv_7533_1";
+                               qcom,intf-type = "dsi";
+                               qcom,assigned_plane = <&reserved_plane1>;
+                       }
+               }
+       }
index cf95a8b..d2240c5 100644 (file)
@@ -28,6 +28,16 @@ config DRM_MSM_REGISTER_LOGGING
          that can be parsed by envytools demsm tool.  If enabled, register
          logging can be switched on via msm.reglog=y module param.
 
+config DRM_MSM_EARLY_CARD
+       bool "Enable Early DRM in MSM DRM driver"
+       depends on DRM_MSM
+       default y
+       help
+         Choose this option if one wants to enable Early DRM driver
+         for MSM/snapdragon.  Early DRM will create one DRI card to
+         support early application.  One should also check device tree
+         to assign proper display resources to early DRM
+
 config DRM_MSM_DSI
        bool "Enable DSI support in MSM DRM driver"
        depends on DRM_MSM
index 6edbca0..dd721cd 100644 (file)
@@ -5,6 +5,7 @@ ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
 ccflags-$(CONFIG_SYNC) += -Idrivers/staging/android
 ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi
 ccflags-y += -Idrivers/gpu/drm/msm/sde
+ccflags-$(CONFIG_DRM_MSM_EARLY_CARD) += -Idrivers/gpu/drm/msm/ekms
 
 msm_drm-y := \
        hdmi/hdmi.o \
@@ -57,6 +58,14 @@ msm_drm-y := \
        sde_edid_parser.o \
        sde_hdcp_1x.o
 
+msm_drm-$(CONFIG_DRM_MSM_EARLY_CARD) += ekms/edrm_kms.o \
+       ekms/edrm_plane.o \
+       ekms/edrm_encoder.o \
+       ekms/edrm_connector.o \
+       ekms/edrm_crtc.o \
+       ekms/edrm_drv.o \
+       ekms/edrm_splash.o
+
 # use drm gpu driver only if qcom_kgsl driver not available
 ifneq ($(CONFIG_QCOM_KGSL),y)
 msm_drm-y += adreno/adreno_device.o \
diff --git a/drivers/gpu/drm/msm/ekms/edrm_connector.c b/drivers/gpu/drm/msm/ekms/edrm_connector.c
new file mode 100644 (file)
index 0000000..8beaa59
--- /dev/null
@@ -0,0 +1,123 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "edrm_connector.h"
+
+struct edrm_connector {
+       struct drm_connector base;
+       struct drm_encoder *encoder;
+       struct msm_edrm_display *display;
+};
+
+#define to_edrm_connector(x) container_of(x, struct edrm_connector, base)
+
+static enum drm_connector_status
+edrm_connector_detect(struct drm_connector *conn, bool force)
+{
+       return connector_status_connected;
+}
+
+static int
+edrm_connector_get_modes(struct drm_connector *connector)
+{
+       struct edrm_connector *edrm_conn = to_edrm_connector(connector);
+       struct drm_display_mode *m;
+
+       m = drm_mode_duplicate(connector->dev, &edrm_conn->display->mode);
+       drm_mode_set_name(m);
+       drm_mode_probed_add(connector, m);
+
+       return 1;
+}
+
+static enum drm_mode_status
+edrm_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static struct drm_encoder *
+edrm_connector_best_encoder(struct drm_connector *connector)
+{
+       struct edrm_connector *edrm_conn = to_edrm_connector(connector);
+
+       return edrm_conn->encoder;
+}
+
+void edrm_connector_destroy(struct drm_connector *connector)
+{
+       struct edrm_connector *edrm_conn = to_edrm_connector(connector);
+
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+       kfree(edrm_conn);
+}
+
+static const struct drm_connector_helper_funcs edrm_connector_helper_funcs = {
+       .get_modes =    edrm_connector_get_modes,
+       .mode_valid =   edrm_mode_valid,
+       .best_encoder = edrm_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs edrm_connector_funcs = {
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = edrm_connector_detect,
+       .destroy = edrm_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+struct drm_connector *edrm_connector_init(struct drm_device *dev,
+                       struct drm_encoder *encoder,
+                       struct msm_edrm_display *display)
+{
+       struct edrm_connector *edrm_conn;
+       struct drm_connector *connector;
+       int ret;
+
+       edrm_conn = kzalloc(sizeof(*edrm_conn), GFP_KERNEL);
+       if (!edrm_conn)
+               return ERR_PTR(-ENOMEM);
+       connector = &edrm_conn->base;
+
+       ret = drm_connector_init(dev, connector,
+                       &edrm_connector_funcs,
+                       display->connector_type);
+       if (ret) {
+               pr_err("edrm drm_connector_init failed\n");
+               goto fail;
+       }
+
+       drm_connector_helper_add(connector, &edrm_connector_helper_funcs);
+
+       edrm_conn->display = display;
+       edrm_conn->encoder = encoder;
+
+       ret = drm_connector_register(&edrm_conn->base);
+       if (ret) {
+               pr_err("failed to register drm connector, %d\n", ret);
+               goto fail;
+       }
+
+       ret = drm_mode_connector_attach_encoder(&edrm_conn->base, encoder);
+       if (ret) {
+               pr_err("failed to attach encoder to connector, %d\n", ret);
+               goto fail;
+       }
+
+       return connector;
+fail:
+       kfree(edrm_conn);
+       return ERR_PTR(ret);
+
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_connector.h b/drivers/gpu/drm/msm/ekms/edrm_connector.h
new file mode 100644 (file)
index 0000000..4bd6deb
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _EDRM_CONNECTOR_H_
+#define _EDRM_CONNECTOR_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "edrm_kms.h"
+
+struct drm_connector *edrm_connector_init(struct drm_device *dev,
+                       struct drm_encoder *encoder,
+                       struct msm_edrm_display *display);
+
+void edrm_connector_destroy(struct drm_connector *connector);
+
+#endif /* _EDRM_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/ekms/edrm_crtc.c b/drivers/gpu/drm/msm/ekms/edrm_crtc.c
new file mode 100644 (file)
index 0000000..b39ec94
--- /dev/null
@@ -0,0 +1,263 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "edrm_crtc.h"
+#include "edrm_plane.h"
+#include "edrm_encoder.h"
+#include "sde_kms.h"
+
+/* display control path Flush register offset */
+#define FLUSH_OFFSET   0x18
+#define SSPP_SRC_FORMAT                    0x30
+#define SSPP_SRC_UNPACK_PATTERN            0x34
+#define SSPP_SRC_OP_MODE                   0x38
+#define SSPP_CONSTANT_COLOR                0x3c
+#define LAYER_BLEND5_OP                    0x260
+#define FLUST_CTL_BIT                      17
+#define LAYER_OP_ENABLE_ALPHA_BLEND        0x600
+
+static void edrm_crtc_plane_attach(struct drm_crtc *crtc,
+       struct drm_plane *plane)
+{
+       struct drm_device *dev = crtc->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+       struct msm_drm_private *master_priv =
+                       edrm_kms->master_dev->dev_private;
+       struct sde_kms *master_kms = to_sde_kms(master_priv->kms);
+       u32 layer_val, ctl_off, lm_idx;
+       struct edrm_plane *edrm_plane = to_edrm_plane(plane);
+       struct edrm_crtc *edrm_crtc = to_edrm_crtc(crtc);
+       struct msm_edrm_display *display;
+
+       display = &edrm_kms->display[edrm_crtc->display_id];
+       ctl_off = display->ctl_off;
+       lm_idx = (display->ctl_id - 1) * 0x4;
+
+       layer_val = readl_relaxed(master_kms->mmio + ctl_off + lm_idx);
+       switch (edrm_plane->sspp_cfg_id) {
+       case 1: /* vig 0 */
+               layer_val |= edrm_plane->lm_stage + 2;
+               break;
+       case 2: /* vig 1 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 3;
+               break;
+       case 3: /* vig 2 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 6;
+               break;
+       case 4: /* vig 3 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 26;
+               break;
+       case 5: /* rgb 0 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 9;
+               break;
+       case 6: /* rgb 1 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 12;
+               break;
+       case 7: /* rgb 2 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 15;
+               break;
+       case 8: /* rgb 3 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 29;
+               break;
+       case 9: /* dma 0 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 18;
+               break;
+       case 10: /* dma 1 */
+               layer_val |= (edrm_plane->lm_stage + 2) << 21;
+               break;
+       }
+       writel_relaxed(layer_val, master_kms->mmio + ctl_off + lm_idx);
+       plane->crtc = crtc;
+}
+
+void edrm_crtc_postinit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       struct msm_edrm_kms *edrm_kms;
+       struct sde_kms *master_kms;
+       struct msm_drm_private *master_priv;
+       struct msm_edrm_display *display;
+       struct edrm_crtc *edrm_crtc;
+       struct edrm_plane *edrm_plane;
+       u32 lm_off, flush_val;
+       const struct drm_plane_helper_funcs *funcs;
+       u32 sspp_flush_mask_bit[10] = {
+                               0, 1, 2, 18, 3, 4, 5, 19, 11, 12};
+
+       edrm_kms = to_edrm_kms(kms);
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+       edrm_plane = to_edrm_plane(crtc->primary);
+       edrm_crtc = to_edrm_crtc(crtc);
+       funcs = crtc->primary->helper_private;
+       funcs->atomic_disable(crtc->primary, crtc->primary->state);
+       display = &edrm_kms->display[edrm_crtc->display_id];
+       lm_off = display->lm_off;
+
+       edrm_crtc_plane_attach(crtc, crtc->primary);
+
+       /* Update CTL bit, layer mixer flush bit and sspp flush bit */
+       flush_val = BIT(FLUST_CTL_BIT);
+       flush_val |= BIT(display->ctl_id + 5);
+       flush_val |= BIT(sspp_flush_mask_bit[edrm_plane->sspp_cfg_id - 1]);
+
+       /* setup alpha blending for mixer stage 5 */
+       writel_relaxed(LAYER_OP_ENABLE_ALPHA_BLEND, master_kms->mmio + lm_off +
+               LAYER_BLEND5_OP);
+       edrm_crtc->sspp_flush_mask |= flush_val;
+
+       edrm_crtc_commit_kickoff(crtc);
+}
+
+static void edrm_crtc_atomic_flush(struct drm_crtc *crtc,
+               struct drm_crtc_state *old_crtc_state)
+{
+       struct drm_plane *plane = NULL;
+
+       if (!crtc) {
+               pr_err("invalid crtc\n");
+               return;
+       }
+
+       /* TODO: wait for acquire fences before anything else is done */
+       drm_atomic_crtc_for_each_plane(plane, crtc) {
+               /* update SSPP bit in sspp_flush_mask */
+               edrm_plane_flush(plane);
+       }
+}
+
+static void edrm_crtc_enable(struct drm_crtc *crtc)
+{
+       crtc->state->enable = true;
+}
+
+static void edrm_crtc_disable(struct drm_crtc *crtc)
+{
+       struct edrm_plane *edrm_plane;
+       struct edrm_crtc *edrm_crtc = to_edrm_crtc(crtc);
+       const struct drm_plane_helper_funcs *funcs;
+       u32 sspp_flush_mask_bit[10] = {
+                               0, 1, 2, 18, 3, 4, 5, 19, 11, 12};
+
+       edrm_plane = to_edrm_plane(crtc->primary);
+       funcs = crtc->primary->helper_private;
+       funcs->atomic_disable(crtc->primary, crtc->primary->state);
+
+       edrm_crtc->sspp_flush_mask |=
+               BIT(sspp_flush_mask_bit[edrm_plane->sspp_cfg_id - 1]);
+       edrm_crtc_commit_kickoff(crtc);
+}
+
+void edrm_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct edrm_crtc *edrm_crtc = to_edrm_crtc(crtc);
+
+       drm_crtc_cleanup(crtc);
+       kfree(edrm_crtc);
+}
+
+static const struct drm_crtc_funcs edrm_crtc_funcs = {
+       .reset =  drm_atomic_helper_crtc_reset,
+       .set_config = drm_atomic_helper_set_config,
+       .destroy = edrm_crtc_destroy,
+       .page_flip = drm_atomic_helper_page_flip,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs edrm_crtc_helper_funcs = {
+       .disable = edrm_crtc_disable,
+       .enable = edrm_crtc_enable,
+       .atomic_flush = edrm_crtc_atomic_flush,
+};
+
+struct drm_crtc *edrm_crtc_init(struct drm_device *dev,
+                                       struct msm_edrm_display *display,
+                                       struct drm_plane *primary_plane)
+{
+       struct edrm_crtc *edrm_crtc;
+       struct drm_crtc *crtc;
+       int ret;
+
+       edrm_crtc = kzalloc(sizeof(*edrm_crtc), GFP_KERNEL);
+       if (!edrm_crtc) {
+               ret = -ENOMEM;
+               goto fail_no_mem;
+       }
+
+       crtc = &edrm_crtc->base;
+       ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
+               &edrm_crtc_funcs);
+       if (ret)
+               goto fail;
+
+       drm_crtc_helper_add(crtc, &edrm_crtc_helper_funcs);
+       edrm_crtc->display_id = display->display_id;
+
+       return crtc;
+fail:
+       kfree(edrm_crtc);
+fail_no_mem:
+       return ERR_PTR(ret);
+}
+
+void edrm_crtc_commit_kickoff(struct drm_crtc *crtc)
+{
+       struct drm_device *dev;
+       struct msm_drm_private *priv;
+       struct msm_edrm_kms *edrm_kms;
+       struct msm_edrm_display *display;
+       struct edrm_crtc *edrm_crtc;
+       struct sde_kms *master_kms;
+       struct msm_drm_private *master_priv;
+       u32 ctl_off;
+
+       dev = crtc->dev;
+       priv = dev->dev_private;
+       edrm_kms = to_edrm_kms(priv->kms);
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+       edrm_crtc = to_edrm_crtc(crtc);
+
+       display = &edrm_kms->display[edrm_crtc->display_id];
+       ctl_off = display->ctl_off;
+
+       /* Trigger the flush */
+       writel_relaxed(edrm_crtc->sspp_flush_mask, master_kms->mmio + ctl_off +
+               FLUSH_OFFSET);
+}
+
+void edrm_crtc_complete_commit(struct drm_crtc *crtc,
+               struct drm_crtc_state *old_state)
+{
+       struct drm_device *dev;
+       struct msm_drm_private *priv;
+       struct drm_encoder *encoder;
+
+       dev = crtc->dev;
+       priv = dev->dev_private;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc != crtc)
+                       continue;
+
+               edrm_encoder_wait_for_commit_done(encoder);
+       }
+}
+
+void edrm_crtc_prepare_commit(struct drm_crtc *crtc,
+                               struct drm_crtc_state *old_state)
+{
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_crtc.h b/drivers/gpu/drm/msm/ekms/edrm_crtc.h
new file mode 100644 (file)
index 0000000..761a6a9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _EDRM_CRTC_H_
+#define _EDRM_CRTC_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "edrm_kms.h"
+
+struct edrm_crtc {
+       struct drm_crtc base;
+       u32 sspp_flush_mask;
+       int display_id;
+};
+
+#define to_edrm_crtc(x) container_of(x, struct edrm_crtc, base)
+
+struct drm_crtc *edrm_crtc_init(struct drm_device *dev,
+       struct msm_edrm_display *display, struct drm_plane *primary_plane);
+
+/**
+ * Helper function to setup the control path
+ * @crtc: Pointer to drm crtc object
+ */
+void edrm_crtc_postinit(struct drm_crtc *crtc);
+
+/**
+ * edrm_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
+ * @crtc: Pointer to drm crtc object
+ */
+void edrm_crtc_commit_kickoff(struct drm_crtc *crtc);
+
+/**
+ * edrm_crtc_complete_commit - callback to prepare for output fences
+ * @crtc: Pointer to drm crtc object
+ * @old_state: Pointer to drm crtc old state object
+ */
+void edrm_crtc_complete_commit(struct drm_crtc *crtc,
+                               struct drm_crtc_state *old_state);
+
+void edrm_crtc_prepare_commit(struct drm_crtc *crtc,
+                               struct drm_crtc_state *old_state);
+
+/**
+ * edrm_crtc_destroy - free up edrm_crtc structure
+ * @crtc: Pointer to drm crtc object
+ */
+void edrm_crtc_destroy(struct drm_crtc *crtc);
+
+#endif /* _EDRM_ENCODER_H_ */
diff --git a/drivers/gpu/drm/msm/ekms/edrm_drv.c b/drivers/gpu/drm/msm/ekms/edrm_drv.c
new file mode 100644 (file)
index 0000000..69b8c01
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_address.h>
+#include <linux/sde_io_util.h>
+#include "msm_drv.h"
+#include "msm_gem.h"
+#include "msm_mmu.h"
+#include "edrm_kms.h"
+
+static int msm_edrm_unload(struct drm_device *dev)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       int i;
+
+       /* clean up display commit worker threads */
+       for (i = 0; i < priv->num_crtcs; i++) {
+               if (priv->disp_thread[i].thread) {
+                       flush_kthread_worker(&priv->disp_thread[i].worker);
+                       kthread_stop(priv->disp_thread[i].thread);
+                       priv->disp_thread[i].thread = NULL;
+               }
+       }
+
+       drm_kms_helper_poll_fini(dev);
+       drm_mode_config_cleanup(dev);
+
+       flush_workqueue(priv->wq);
+       destroy_workqueue(priv->wq);
+
+       if (kms)
+               kms->funcs->destroy(kms);
+
+       dev->dev_private = NULL;
+
+       kfree(priv);
+
+       return 0;
+}
+
+static int msm_edrm_load(struct drm_device *dev, unsigned long flags)
+{
+       struct platform_device *pdev = dev->platformdev;
+       struct msm_drm_private *priv;
+       struct msm_kms *kms;
+       struct drm_device *master_dev;
+       struct msm_drm_private *master_priv;
+       struct drm_minor *minor;
+       int ret, i;
+       struct sched_param param;
+
+       /* main DRM's minor ID is zero */
+       minor = drm_minor_acquire(0);
+       if (IS_ERR(minor)) {
+               pr_err("master drm_minor has no dev, stop early drm loading\n");
+               return -ENODEV;
+       }
+       master_dev = minor->dev;
+       drm_minor_release(minor);
+       master_priv = master_dev->dev_private;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       dev->dev_private = priv;
+
+       priv->wq = alloc_ordered_workqueue("msm_edrm", 0);
+       init_waitqueue_head(&priv->fence_event);
+       init_waitqueue_head(&priv->pending_crtcs_event);
+       INIT_LIST_HEAD(&priv->client_event_list);
+       INIT_LIST_HEAD(&priv->inactive_list);
+       INIT_LIST_HEAD(&priv->fence_cbs);
+       hash_init(priv->mn_hash);
+       mutex_init(&priv->mn_lock);
+
+       drm_mode_config_init(dev);
+
+       platform_set_drvdata(pdev, dev);
+       priv->pclient = master_priv->pclient;
+       memcpy((void *)&priv->phandle.mp, (void *) &master_priv->phandle.mp,
+               sizeof(struct dss_module_power));
+       INIT_LIST_HEAD(&priv->phandle.power_client_clist);
+       mutex_init(&priv->phandle.phandle_lock);
+
+       priv->vram.size = 0;
+       kms = msm_edrm_kms_init(dev);
+       if (IS_ERR(kms)) {
+               priv->kms = NULL;
+               dev_err(dev->dev, "failed to load kms\n");
+               ret = PTR_ERR(kms);
+               goto fail;
+       }
+
+       priv->kms = kms;
+       if (kms && kms->funcs && kms->funcs->hw_init) {
+               ret = kms->funcs->hw_init(kms);
+               if (ret) {
+                       dev_err(dev->dev, "kms hw init failed: %d\n", ret);
+                       goto fail;
+               }
+       }
+
+       /**
+        * this priority was found during empiric testing to have appropriate
+        * realtime scheduling to process display updates and interact with
+        * other real time and normal priority task
+        */
+       param.sched_priority = 16;
+       /* initialize commit thread structure */
+       for (i = 0; i < priv->num_crtcs; i++) {
+               priv->disp_thread[i].crtc_id = priv->crtcs[i]->base.id;
+               init_kthread_worker(&priv->disp_thread[i].worker);
+               priv->disp_thread[i].dev = dev;
+               priv->disp_thread[i].thread =
+                       kthread_run(kthread_worker_fn,
+                               &priv->disp_thread[i].worker,
+                               "crtc_commit:%d",
+                               priv->disp_thread[i].crtc_id);
+               ret = sched_setscheduler(priv->disp_thread[i].thread,
+                                                       SCHED_FIFO, &param);
+               if (ret)
+                       pr_warn("display thread priority update failed: %d\n",
+                                                                       ret);
+
+               if (IS_ERR(priv->disp_thread[i].thread)) {
+                       dev_err(dev->dev, "failed to create kthread\n");
+                       priv->disp_thread[i].thread = NULL;
+                       /* clean up previously created threads if any */
+                       for (i -= 1; i >= 0; i--) {
+                               kthread_stop(priv->disp_thread[i].thread);
+                               priv->disp_thread[i].thread = NULL;
+                       }
+                       goto fail;
+               }
+       }
+
+       /* share same function from master drm */
+       dev->mode_config.funcs = master_dev->mode_config.funcs;
+
+       ret = drm_vblank_init(dev, priv->num_crtcs);
+       if (ret < 0) {
+               dev_err(dev->dev, "failed to initialize vblank\n");
+               goto fail;
+       }
+
+       drm_mode_config_reset(dev);
+       /* perform subdriver post initialization */
+       if (kms && kms->funcs && kms->funcs->postinit) {
+               ret = kms->funcs->postinit(kms);
+               if (ret) {
+                       dev_err(dev->dev, "kms post init failed: %d\n", ret);
+                       goto fail;
+               }
+       }
+
+       drm_kms_helper_poll_init(dev);
+       return 0;
+
+fail:
+       msm_edrm_unload(dev);
+       return ret;
+}
+
+static int msm_edrm_open(struct drm_device *dev, struct drm_file *file)
+{
+       struct msm_file_private *ctx = NULL;
+       struct msm_drm_private *priv;
+       struct msm_kms *kms;
+
+       if (!dev || !dev->dev_private)
+               return -ENODEV;
+       priv = dev->dev_private;
+
+       file->driver_priv = ctx;
+       kms = priv->kms;
+
+       if (kms) {
+               struct msm_edrm_kms *edrm_kms;
+
+               edrm_kms = to_edrm_kms(kms);
+               /* return failure if eDRM already handoff display resource
+                * to main DRM
+                */
+               if (edrm_kms->handoff_flag)
+                       return -ENODEV;
+       }
+
+       if (kms && kms->funcs && kms->funcs->postopen)
+               kms->funcs->postopen(kms, file);
+
+       return 0;
+}
+
+static void msm_preclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+
+       if (kms && kms->funcs && kms->funcs->preclose)
+               kms->funcs->preclose(kms, file);
+}
+
+static void msm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_file_private *ctx = file->driver_priv;
+       struct msm_kms *kms = priv->kms;
+
+       if (kms && kms->funcs && kms->funcs->postclose)
+               kms->funcs->postclose(kms, file);
+
+       if (!ctx)
+               return;
+
+       kfree(ctx);
+}
+
+static void msm_lastclose(struct drm_device *dev)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+
+       struct msm_kms *kms = priv->kms;
+
+       /* wait for pending vblank requests to be executed by worker thread */
+       flush_workqueue(priv->wq);
+
+       if (kms && kms->funcs && kms->funcs->lastclose)
+               kms->funcs->lastclose(kms);
+}
+
+static int msm_edrm_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+       return 0;
+}
+
+static void msm_edrm_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+}
+
+
+static const struct vm_operations_struct vm_ops = {
+       .fault = msm_gem_fault,
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static const struct file_operations fops = {
+       .owner              = THIS_MODULE,
+       .open               = drm_open,
+       .release            = drm_release,
+       .unlocked_ioctl     = drm_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl       = drm_compat_ioctl,
+#endif
+       .poll               = drm_poll,
+       .read               = drm_read,
+       .llseek             = no_llseek,
+       .mmap               = msm_gem_mmap,
+};
+
+static struct drm_driver msm_edrm_driver = {
+       .driver_features    = DRIVER_HAVE_IRQ |
+                               DRIVER_GEM |
+                               DRIVER_PRIME |
+                               DRIVER_RENDER |
+                               DRIVER_ATOMIC |
+                               DRIVER_MODESET,
+       .load               = msm_edrm_load,
+       .unload             = msm_edrm_unload,
+       .open               = msm_edrm_open,
+       .preclose           = msm_preclose,
+       .postclose          = msm_postclose,
+       .lastclose          = msm_lastclose,
+       .set_busid          = drm_platform_set_busid,
+       .get_vblank_counter = drm_vblank_no_hw_counter,
+       .enable_vblank      = msm_edrm_enable_vblank,
+       .disable_vblank     = msm_edrm_disable_vblank,
+       .gem_free_object    = msm_gem_free_object,
+       .gem_vm_ops         = &vm_ops,
+       .dumb_create        = msm_gem_dumb_create,
+       .dumb_map_offset    = msm_gem_dumb_map_offset,
+       .dumb_destroy       = drm_gem_dumb_destroy,
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_prime_export   = drm_gem_prime_export,
+       .gem_prime_import   = drm_gem_prime_import,
+       .gem_prime_res_obj  = msm_gem_prime_res_obj,
+       .gem_prime_pin      = msm_gem_prime_pin,
+       .gem_prime_unpin    = msm_gem_prime_unpin,
+       .gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
+       .gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
+       .gem_prime_vmap     = msm_gem_prime_vmap,
+       .gem_prime_vunmap   = msm_gem_prime_vunmap,
+       .gem_prime_mmap     = msm_gem_prime_mmap,
+
+       .ioctls             = NULL,
+       .num_ioctls         = 0,
+       .fops               = &fops,
+       .name               = "msm",
+       .desc               = "MSM Snapdragon DRM",
+       .date               = "20181024",
+       .major              = 1,
+       .minor              = 1,
+};
+
+static int msm_pdev_edrm_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct drm_minor *minor;
+       struct drm_device *master_dev;
+       struct msm_drm_private *master_priv;
+       struct msm_kms *master_kms;
+
+       /* main DRM's minor ID is zero */
+       minor = drm_minor_acquire(0);
+       if (IS_ERR(minor)) {
+               pr_err("drm_minor has no dev, defer the probe\n");
+               return -EPROBE_DEFER;
+       }
+       master_dev = minor->dev;
+       drm_minor_release(minor);
+       if (!master_dev) {
+               pr_err("master_dev is null, defer the probe\n");
+               return -EPROBE_DEFER;
+       }
+
+       master_priv = master_dev->dev_private;
+       if (!master_priv) {
+               pr_err("master_priv is null, defer the probe\n");
+               return -EPROBE_DEFER;
+       }
+
+       master_kms = master_priv->kms;
+       if (!master_kms) {
+               pr_err("master KMS is null, defer the probe\n");
+               return -EPROBE_DEFER;
+       }
+
+       /* on all devices that I am aware of, iommu's which cna map
+        * any address the cpu can see are used:
+        */
+       ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
+       if (ret) {
+               pr_err("dma_set_mask_and_coherent return %d\n", ret);
+               return ret;
+       }
+
+       ret = drm_platform_init(&msm_edrm_driver,
+                       to_platform_device(&pdev->dev));
+       if (ret)
+               DRM_ERROR("drm_platform_init failed: %d\n", ret);
+
+       return ret;
+}
+
+static int msm_pdev_edrm_remove(struct platform_device *pdev)
+{
+       drm_put_dev(platform_get_drvdata(to_platform_device(&pdev->dev)));
+       return 0;
+}
+
+static const struct platform_device_id msm_edrm_id[] = {
+       { "edrm_mdp", 0 },
+       { }
+};
+
+static void msm_edrm_lastclose(struct drm_device *dev)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+
+       if (kms && kms->funcs && kms->funcs->lastclose)
+               kms->funcs->lastclose(kms);
+}
+
+static void msm_pdev_edrm_shutdown(struct platform_device *pdev)
+{
+       struct drm_device *ddev = platform_get_drvdata(pdev);
+       struct msm_drm_private *priv = NULL;
+
+       priv = ddev->dev_private;
+       msm_edrm_lastclose(ddev);
+
+       /* set this after lastclose to allow kickoff from lastclose */
+       priv->shutdown_in_progress = true;
+}
+
+static const struct of_device_id dt_match[] = {
+       { .compatible = "qcom,msm-kms-edrm" },  /* sde  */
+       {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static struct platform_driver msm_platform_driver = {
+       .probe      = msm_pdev_edrm_probe,
+       .remove     = msm_pdev_edrm_remove,
+       .shutdown   = msm_pdev_edrm_shutdown,
+       .driver     = {
+               .name   = "msm_early_drm",
+               .of_match_table = dt_match,
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+       },
+       .id_table   = msm_edrm_id,
+};
+
+static int __init msm_edrm_register(void)
+{
+       DBG("init");
+       return platform_driver_register(&msm_platform_driver);
+}
+
+static void __exit msm_edrm_unregister(void)
+{
+       DBG("fini");
+       platform_driver_unregister(&msm_platform_driver);
+}
+
+module_init(msm_edrm_register);
+module_exit(msm_edrm_unregister);
+
+MODULE_DESCRIPTION("MSM EARLY DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/msm/ekms/edrm_encoder.c b/drivers/gpu/drm/msm/ekms/edrm_encoder.c
new file mode 100644 (file)
index 0000000..0cee78c
--- /dev/null
@@ -0,0 +1,112 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "edrm_encoder.h"
+#include "edrm_crtc.h"
+#include "sde_kms.h"
+
+static void edrm_encoder_enable(struct drm_encoder *drm_enc)
+{
+       pr_err("eDRM Encoder enable\n");
+}
+
+static void edrm_encoder_disable(struct drm_encoder *drm_enc)
+{
+       pr_err("eDRM Encoder disable\n");
+}
+
+void edrm_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct edrm_encoder *edrm_enc = to_edrm_encoder(encoder);
+
+       drm_encoder_cleanup(encoder);
+       kfree(edrm_enc);
+}
+
+static const struct drm_encoder_helper_funcs edrm_encoder_helper_funcs = {
+       .disable = edrm_encoder_disable,
+       .enable = edrm_encoder_enable,
+};
+
+static const struct drm_encoder_funcs edrm_encoder_funcs = {
+       .destroy = edrm_encoder_destroy,
+};
+
+int edrm_encoder_wait_for_commit_done(struct drm_encoder *drm_enc)
+{
+       struct drm_device *dev;
+       struct msm_drm_private *priv;
+       struct msm_edrm_kms *edrm_kms;
+       struct msm_edrm_display *display;
+       struct edrm_crtc *edrm_crtc;
+       struct sde_kms *master_kms;
+       struct msm_drm_private *master_priv;
+       struct sde_mdss_cfg *cfg;
+       u32 ctl_off;
+       u32 flush_register = 0;
+       int i;
+
+       dev = drm_enc->dev;
+       priv = dev->dev_private;
+       edrm_kms = to_edrm_kms(priv->kms);
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+       cfg = master_kms->catalog;
+       edrm_crtc = to_edrm_crtc(drm_enc->crtc);
+       display = &edrm_kms->display[edrm_crtc->display_id];
+       ctl_off = display->ctl_off;
+
+       /* poll edrm_crtc->sspp_flush_mask until cleared */
+       for (i = 0; i < 20; i++) {
+               flush_register = readl_relaxed(master_kms->mmio +
+                               ctl_off + 0x18);
+               if ((flush_register & edrm_crtc->sspp_flush_mask) != 0)
+                       usleep_range(1000, 2000);
+               else
+                       break;
+       }
+
+       /* reset sspp_flush_mask */
+       edrm_crtc->sspp_flush_mask = 0;
+
+       return 0;
+}
+
+
+struct drm_encoder *edrm_encoder_init(struct drm_device *dev,
+                                       struct msm_edrm_display *display)
+{
+       struct edrm_encoder *edrm_encoder;
+       struct drm_encoder *encoder;
+       int ret;
+
+       edrm_encoder = kzalloc(sizeof(*edrm_encoder), GFP_KERNEL);
+       if (!edrm_encoder)
+               return ERR_PTR(-ENOMEM);
+
+       encoder = &edrm_encoder->base;
+
+       ret = drm_encoder_init(dev, encoder,
+                       &edrm_encoder_funcs,
+                       display->encoder_type);
+       if (ret)
+               goto fail;
+
+       drm_encoder_helper_add(encoder, &edrm_encoder_helper_funcs);
+
+       edrm_encoder->intf_idx = display->intf_id;
+
+       return encoder;
+fail:
+       kfree(edrm_encoder);
+       return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_encoder.h b/drivers/gpu/drm/msm/ekms/edrm_encoder.h
new file mode 100644 (file)
index 0000000..eeb91d6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _EDRM_ENCODER_H_
+#define _EDRM_ENCODER_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "edrm_kms.h"
+
+struct edrm_encoder {
+       struct drm_encoder base;
+       u32 sspp_mask;
+       int intf_idx;
+};
+
+#define to_edrm_encoder(x) container_of(x, struct edrm_encoder, base)
+
+/**
+ * edrm_encoder_wait_for_commit_done - wait until the register flush is done
+ * @drm_enc: Pointer to drm_encoder object
+ */
+int edrm_encoder_wait_for_commit_done(struct drm_encoder *drm_enc);
+
+/**
+ * edrm_encoder_destroy - free up drm_encoder object
+ * @drm_enc: Pointer to drm encoder object
+ */
+void edrm_encoder_destroy(struct drm_encoder *encoder);
+
+/**
+ * edrm_encoder_init - create drm_encoder object
+ * @dev: drm_device that this encoder going to register.
+ * @display: display structure that associate with this encoder.
+ */
+struct drm_encoder *edrm_encoder_init(struct drm_device *dev,
+                                       struct msm_edrm_display *display);
+
+#endif /* _EDRM_ENCODER_H_ */
diff --git a/drivers/gpu/drm/msm/ekms/edrm_kms.c b/drivers/gpu/drm/msm/ekms/edrm_kms.c
new file mode 100644 (file)
index 0000000..3aa4577
--- /dev/null
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <drm/drm_crtc.h>
+#include <linux/debugfs.h>
+#include <soc/qcom/boot_stats.h>
+#include "msm_kms.h"
+#include "edrm_kms.h"
+#include "edrm_crtc.h"
+#include "edrm_encoder.h"
+#include "edrm_plane.h"
+#include "edrm_connector.h"
+#include "sde_kms.h"
+#include "sde_formats.h"
+#include "edrm_splash.h"
+#include "sde_hdmi.h"
+#include "dsi_display.h"
+#include "sde_crtc.h"
+
+#define MMSS_MDP_CTL_TOP_OFFSET 0x14
+
+static bool first_commit = true;
+
+static void edrm_kms_prepare_commit(struct msm_kms *kms,
+               struct drm_atomic_state *state)
+{
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+       struct drm_device *dev = edrm_kms->master_dev;
+       struct msm_drm_private *master_priv = edrm_kms->master_dev->dev_private;
+       struct sde_kms *master_kms;
+       int i, nplanes;
+       struct drm_plane *plane;
+       bool valid_commit = false;
+
+       master_kms = to_sde_kms(master_priv->kms);
+       nplanes = dev->mode_config.num_total_plane;
+       for (i = 0; i < nplanes; i++) {
+               plane = state->planes[i];
+               if (plane && plane->fb) {
+                       valid_commit = true;
+                       break;
+               }
+       }
+
+       if (valid_commit && first_commit) {
+               first_commit = false;
+               place_marker("eDRM display first valid commit");
+       }
+
+       sde_power_resource_enable(&master_priv->phandle,
+                       master_kms->core_client, true);
+
+       /* Notify bootloader splash to stop */
+       if (valid_commit && edrm_kms->lk_running_flag) {
+
+               /* if LK is still running, notify LK to stop */
+               if (edrm_splash_get_lk_status(kms) !=
+                       SPLASH_STATUS_NOT_START) {
+                       edrm_splash_notify_lk_stop_splash(kms);
+                       edrm_splash_poll_lk_stop_splash(kms);
+               }
+
+               /* next eDRM close will trigger display resources handoff */
+               edrm_kms->handoff_flag = true;
+       }
+}
+
+static void edrm_kms_commit(struct msm_kms *kms,
+               struct drm_atomic_state *old_state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       int i;
+
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               if (crtc->state->active)
+                       edrm_crtc_commit_kickoff(crtc);
+       }
+}
+
+static void edrm_kms_complete_commit(struct msm_kms *kms,
+               struct drm_atomic_state *old_state)
+{
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+       struct msm_drm_private *master_priv = edrm_kms->master_dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       struct sde_kms *master_kms;
+       int i;
+
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i)
+               edrm_crtc_complete_commit(crtc, old_crtc_state);
+
+       master_kms = to_sde_kms(master_priv->kms);
+       sde_power_resource_enable(&master_priv->phandle,
+               master_kms->core_client, false);
+}
+
+static void edrm_kms_wait_for_commit_done(struct msm_kms *kms,
+               struct drm_crtc *crtc)
+{
+       struct drm_encoder *encoder;
+       struct drm_device *dev;
+       int ret;
+
+       dev = crtc->dev;
+       if (!dev)
+               return;
+
+       if (!crtc->state->enable) {
+               pr_err("[crtc:%d] not enable\n", crtc->base.id);
+               return;
+       }
+
+       if (!crtc->state->active) {
+               pr_err("[crtc:%d] not active\n", crtc->base.id);
+               return;
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc != crtc)
+                       continue;
+               ret = edrm_encoder_wait_for_commit_done(encoder);
+               if (ret && ret != -EWOULDBLOCK) {
+                       pr_err("wait for commit done returned %d\n", ret);
+                       break;
+               }
+       }
+}
+
+static void edrm_kms_prepare_fence(struct msm_kms *kms,
+               struct drm_atomic_state *old_state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       int i;
+
+       if (!kms || !old_state || !old_state->dev || !old_state->acquire_ctx) {
+               pr_err("invalid argument(s)\n");
+               return;
+       }
+
+       /* old_state contains updated crtc pointers */
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i)
+               edrm_crtc_prepare_commit(crtc, old_crtc_state);
+}
+
+static void _edrm_kms_drm_obj_destroy(struct msm_edrm_kms *edrm_kms)
+{
+       struct msm_drm_private *priv;
+       int i;
+
+       if (!edrm_kms) {
+               pr_err("invalid sde_kms\n");
+               return;
+       } else if (!edrm_kms->dev) {
+               pr_err("invalid dev\n");
+               return;
+       } else if (!edrm_kms->dev->dev_private) {
+               pr_err("invalid dev_private\n");
+               return;
+       }
+       priv = edrm_kms->dev->dev_private;
+
+       for (i = 0; i < priv->num_crtcs; i++)
+               priv->crtcs[i]->funcs->destroy(priv->crtcs[i]);
+       priv->num_crtcs = 0;
+
+       for (i = 0; i < priv->num_planes; i++)
+               priv->planes[i]->funcs->destroy(priv->planes[i]);
+       priv->num_planes = 0;
+
+       for (i = 0; i < priv->num_connectors; i++)
+               priv->connectors[i]->funcs->destroy(priv->connectors[i]);
+       priv->num_connectors = 0;
+
+       for (i = 0; i < priv->num_encoders; i++)
+               priv->encoders[i]->funcs->destroy(priv->encoders[i]);
+       priv->num_encoders = 0;
+}
+
+static void convert_dsi_to_drm_mode(const struct dsi_display_mode *dsi_mode,
+                               struct drm_display_mode *drm_mode)
+{
+       memset(drm_mode, 0, sizeof(*drm_mode));
+
+       drm_mode->hdisplay = dsi_mode->timing.h_active;
+       drm_mode->hsync_start = drm_mode->hdisplay +
+                               dsi_mode->timing.h_front_porch;
+       drm_mode->hsync_end = drm_mode->hsync_start +
+                               dsi_mode->timing.h_sync_width;
+       drm_mode->htotal = drm_mode->hsync_end + dsi_mode->timing.h_back_porch;
+       drm_mode->hskew = dsi_mode->timing.h_skew;
+
+       drm_mode->vdisplay = dsi_mode->timing.v_active;
+       drm_mode->vsync_start = drm_mode->vdisplay +
+                               dsi_mode->timing.v_front_porch;
+       drm_mode->vsync_end = drm_mode->vsync_start +
+                             dsi_mode->timing.v_sync_width;
+       drm_mode->vtotal = drm_mode->vsync_end + dsi_mode->timing.v_back_porch;
+
+       drm_mode->vrefresh = dsi_mode->timing.refresh_rate;
+       drm_mode->clock = dsi_mode->pixel_clk_khz;
+
+       if (dsi_mode->flags & DSI_MODE_FLAG_SEAMLESS)
+               drm_mode->flags |= DRM_MODE_FLAG_SEAMLESS;
+       if (dsi_mode->flags & DSI_MODE_FLAG_DFPS)
+               drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS;
+       if (dsi_mode->flags & DSI_MODE_FLAG_VBLANK_PRE_MODESET)
+               drm_mode->private_flags |= MSM_MODE_FLAG_VBLANK_PRE_MODESET;
+       drm_mode->flags |= (dsi_mode->timing.h_sync_polarity) ?
+                               DRM_MODE_FLAG_NHSYNC : DRM_MODE_FLAG_PHSYNC;
+       drm_mode->flags |= (dsi_mode->timing.v_sync_polarity) ?
+                               DRM_MODE_FLAG_NVSYNC : DRM_MODE_FLAG_PVSYNC;
+
+       drm_mode_set_name(drm_mode);
+}
+
+static int setup_edrm_displays(struct sde_kms *master_kms,
+                       struct msm_edrm_display *display,
+                       const char *label, const char *type)
+{
+       int i, ret;
+       struct dsi_display *dsi_disp;
+       struct sde_hdmi *hdmi_display;
+       struct sde_mdss_cfg *cfg;
+       u32 reg_value;
+
+       cfg = master_kms->catalog;
+       ret = -EINVAL;
+       /* check main DRM for the matching display */
+       if (!strcmp(type, "dsi")) {
+               int mode_cnt;
+               struct dsi_display_mode *dsi_mode;
+               /* check main DRM's DSI display list */
+               for (i = 0; i < master_kms->dsi_display_count; i++) {
+                       dsi_disp = (struct dsi_display *)
+                               master_kms->dsi_displays[i];
+                       if (!strcmp(dsi_disp->name, label)) {
+                               dsi_display_get_modes(dsi_disp, NULL,
+                                       &mode_cnt);
+                               dsi_mode = kcalloc(mode_cnt, sizeof(*dsi_mode),
+                                       GFP_KERNEL);
+                               if (!dsi_mode)
+                                       return -ENOMEM;
+                               dsi_display_get_modes(dsi_disp, dsi_mode,
+                                       &mode_cnt);
+
+                               /* convert to DRM mode */
+                               convert_dsi_to_drm_mode(&dsi_mode[0],
+                                       &display->mode);
+                               display->encoder_type = DRM_MODE_ENCODER_DSI;
+                               display->connector_type =
+                                               DRM_MODE_CONNECTOR_DSI;
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret) {
+                       pr_err("Cannot find %s in main DRM\n", label);
+                       return ret;
+               }
+               ret = -EINVAL;
+               for (i = 0; i < cfg->ctl_count; i++) {
+                       reg_value = readl_relaxed(master_kms->mmio +
+                               cfg->ctl[i].base + MMSS_MDP_CTL_TOP_OFFSET);
+                       reg_value &= 0x000000F0;
+
+                       /* Check the interface from TOP register */
+                       if ((((reg_value >> 4) == 0x2) &&
+                               (dsi_disp->ctrl[0].ctrl->index == 0)) ||
+                               (((reg_value >> 4) == 0x3) &&
+                               (dsi_disp->ctrl[0].ctrl->index == 1))) {
+                               display->ctl_id = i + 1;
+                               display->ctl_off = cfg->ctl[i].base;
+                               display->lm_off = cfg->mixer[i].base;
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret) {
+                       pr_err("LK does not enable %s\n", label);
+                       kfree(dsi_mode);
+                       return -EINVAL;
+               }
+       } else if (!strcmp(type, "hdmi")) {
+               /* for HDMI interface, check main DRM's HDMI display list */
+               for (i = 0; i < master_kms->hdmi_display_count; i++) {
+                       hdmi_display = (struct sde_hdmi *)
+                               master_kms->hdmi_displays[i];
+
+                       if (!strcmp(hdmi_display->name, label)) {
+                               drm_mode_copy(&display->mode,
+                                       (struct drm_display_mode *)
+                                       hdmi_display->mode_list.next);
+                               display->encoder_type = DRM_MODE_ENCODER_TMDS;
+                               display->connector_type =
+                                       DRM_MODE_CONNECTOR_HDMIA;
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret) {
+                       pr_err("Cannot find %s in main DRM\n", label);
+                       return ret;
+               }
+               ret = -EINVAL;
+               for (i = 0; i < cfg->ctl_count; i++) {
+                       reg_value = readl_relaxed(master_kms->mmio +
+                               cfg->ctl[i].base + MMSS_MDP_CTL_TOP_OFFSET);
+                       reg_value &= 0x000000F0;
+
+                       /* Check the interface from TOP register */
+                       if ((reg_value >> 4) == 0x4) {
+                               display->ctl_id = i + 1;
+                               display->ctl_off = cfg->ctl[i].base;
+                               display->lm_off = cfg->mixer[i].base;
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret) {
+                       pr_err("No LK does not enable %s\n", label);
+                       return -EINVAL;
+               }
+       }
+       return ret;
+}
+
+static int _sspp_search(const char *p_name, struct sde_mdss_cfg *cfg,
+                       u32 *sspp_offset, u32 *sspp_cfg_id, u32 *sspp_type)
+{
+       int i, ret;
+
+       ret = -1;
+       for (i = 0; i < cfg->sspp_count; i++)
+               if (!strcmp(cfg->sspp[i].name, p_name)) {
+                       *sspp_offset = cfg->sspp[i].base;
+                       *sspp_cfg_id = cfg->sspp[i].id;
+                       *sspp_type = cfg->sspp[i].type;
+                       ret = 0;
+                       break;
+               }
+       return ret;
+}
+
+static int _edrm_kms_parse_dt(struct msm_edrm_kms *edrm_kms)
+{
+       struct sde_kms *master_kms;
+       struct msm_drm_private *master_priv;
+       struct msm_drm_private *priv;
+       struct sde_mdss_cfg *cfg;
+       struct device_node *parent, *node;
+       int i, ret, disp_cnt, plane_cnt;
+       const char *clabel;
+       const char *ctype;
+       struct device_node *plane_node;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       struct edrm_plane *edrm_plane;
+       const char *p_name;
+       u32 lm_stage, sspp_offset, sspp_cfg_id, sspp_type;
+
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+       priv = edrm_kms->dev->dev_private;
+       cfg = master_kms->catalog;
+       ret = 0;
+       parent = of_get_child_by_name(edrm_kms->dev->dev->of_node,
+               "qcom,edrm-assigned-display");
+       if (!parent) {
+               pr_err("cannot find qcom,edrm-assigned-display\n");
+               return 0;
+       }
+
+       /* parse the dtsi and retrieve information from main DRM */
+       disp_cnt = 0;
+       for_each_child_of_node(parent, node) {
+               of_property_read_string(node, "qcom,intf-type", &ctype);
+               of_property_read_string(node, "qcom,label", &clabel);
+
+               plane_cnt = 0;
+               do {
+                       plane_node = of_parse_phandle(node,
+                               "qcom,assigned_plane", plane_cnt);
+                       /* Initialize plane */
+                       if (!plane_node)
+                               break;
+
+                       of_property_read_string(plane_node, "qcom,plane-name",
+                                       &p_name);
+                       of_property_read_u32(plane_node, "lm-stage",
+                                       &lm_stage);
+                       if (_sspp_search(p_name, cfg, &sspp_offset,
+                               &sspp_cfg_id, &sspp_type)) {
+                               pr_err("Cannot find %s in main DRM\n",
+                                       p_name);
+                               continue;
+                       }
+
+                       plane = edrm_plane_init(edrm_kms->dev,
+                                       edrm_kms->plane_id[disp_cnt]);
+                       if (IS_ERR(plane)) {
+                               pr_err("edrm_plane_init failed\n");
+                               ret = PTR_ERR(plane);
+                               of_node_put(plane_node);
+                               goto fail;
+                       }
+                       priv->planes[priv->num_planes] = plane;
+                       edrm_plane = to_edrm_plane(plane);
+                       edrm_plane->display_id = disp_cnt;
+                       edrm_plane->lm_stage = lm_stage;
+                       edrm_plane->sspp_offset = sspp_offset;
+                       edrm_plane->sspp_cfg_id = sspp_cfg_id;
+                       plane->possible_crtcs = (1 << disp_cnt);
+                       priv->num_planes++;
+                       plane_cnt++;
+                       of_node_put(plane_node);
+               } while (plane_node);
+
+               edrm_kms->display[disp_cnt].plane_cnt = plane_cnt;
+               ret = setup_edrm_displays(master_kms,
+                       &edrm_kms->display[disp_cnt], clabel, ctype);
+               if (ret)
+                       goto fail;
+
+               /* Initialize crtc */
+               crtc = edrm_crtc_init(edrm_kms->dev,
+                       &edrm_kms->display[disp_cnt], priv->planes[disp_cnt]);
+               if (IS_ERR(crtc)) {
+                       ret = PTR_ERR(crtc);
+                       goto fail;
+               }
+               priv->crtcs[priv->num_crtcs++] = crtc;
+
+               /* Initialize encoder */
+               encoder = edrm_encoder_init(edrm_kms->dev,
+                       &edrm_kms->display[disp_cnt]);
+               if (IS_ERR(encoder)) {
+                       ret = PTR_ERR(encoder);
+                       goto fail;
+               }
+               encoder->possible_crtcs = (1 << disp_cnt);
+               priv->encoders[priv->num_encoders++] = encoder;
+
+               /* Initialize connector */
+               connector = edrm_connector_init(edrm_kms->dev,
+                               priv->encoders[disp_cnt],
+                               &edrm_kms->display[disp_cnt]);
+               if (IS_ERR(encoder)) {
+                       ret = PTR_ERR(connector);
+                       goto fail;
+               }
+               priv->connectors[priv->num_connectors++] = connector;
+
+               disp_cnt++;
+       }
+       of_node_put(parent);
+
+       edrm_kms->display_count = disp_cnt;
+       edrm_kms->plane_count = priv->num_planes;
+       return ret;
+fail:
+       for (i = 0; i < priv->num_planes; i++)
+               edrm_plane_destroy(priv->planes[i]);
+       priv->num_planes = 0;
+
+       for (i = 0; i < disp_cnt; i++) {
+               if (priv->crtcs[i]) {
+                       edrm_crtc_destroy(priv->crtcs[i]);
+                       priv->num_crtcs--;
+               }
+               if (priv->encoders[i]) {
+                       edrm_encoder_destroy(priv->encoders[i]);
+                       priv->num_encoders--;
+               }
+               if (priv->connectors[i]) {
+                       edrm_connector_destroy(priv->connectors[i]);
+                       priv->num_connectors--;
+               }
+       }
+       disp_cnt = 0;
+       edrm_kms->display_count = 0;
+       edrm_kms->plane_count = 0;
+       of_node_put(parent);
+       return ret;
+}
+
+static int _edrm_kms_drm_obj_init(struct msm_edrm_kms *edrm_kms)
+{
+       struct drm_device *dev;
+       struct msm_drm_private *priv;
+       int ret;
+
+       if (!edrm_kms || !edrm_kms->dev || !edrm_kms->dev->dev) {
+               pr_err("invalid edrm_kms\n");
+               return -EINVAL;
+       }
+
+       dev = edrm_kms->dev;
+       priv = dev->dev_private;
+
+       ret = _edrm_kms_parse_dt(edrm_kms);
+       if (ret)
+               goto fail;
+
+       return 0;
+fail:
+       _edrm_kms_drm_obj_destroy(edrm_kms);
+       return ret;
+}
+
+static int edrm_kms_postinit(struct msm_kms *kms)
+{
+       struct drm_device *dev;
+       struct drm_crtc *crtc;
+       struct msm_edrm_kms *edrm_kms;
+
+       edrm_kms = to_edrm_kms(kms);
+       dev = edrm_kms->dev;
+
+       drm_for_each_crtc(crtc, dev)
+               edrm_crtc_postinit(crtc);
+
+       place_marker("eDRM driver init completed");
+       return 0;
+}
+
+static void edrm_kms_destroy(struct msm_kms *kms)
+{
+       struct msm_edrm_kms *edrm_kms;
+       struct drm_device *dev;
+
+       if (!kms) {
+               pr_err("edrm_kms_destroy invalid kms\n");
+               return;
+       }
+
+       edrm_kms = to_edrm_kms(kms);
+       dev = edrm_kms->dev;
+       if (!dev) {
+               pr_err("invalid device\n");
+               return;
+       }
+
+       kfree(edrm_kms);
+}
+
+static void edrm_kms_lastclose(struct msm_kms *kms)
+{
+       /* handoff early drm resource */
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+
+       /* notify main DRM that eDRM is relased.  main DRM can
+        * reclaim all eDRM resource. Main DRM will clear eDRM
+        * plane stage in next commit
+        */
+       if (edrm_kms->handoff_flag) {
+               pr_info("handoff eDRM resource to main DRM\n");
+               edrm_display_release(kms);
+       }
+}
+
+static int edrm_kms_hw_init(struct msm_kms *kms)
+{
+       struct msm_edrm_kms *edrm_kms;
+       struct sde_kms *sde_kms;
+       struct drm_device *dev;
+       struct msm_drm_private *priv;
+       struct msm_drm_private *master_priv;
+       int rc = -EINVAL;
+       u32 lk_status;
+
+       if (!kms) {
+               pr_err("edrm_kms_hw_init invalid kms\n");
+               goto error;
+       }
+
+       edrm_kms = to_edrm_kms(kms);
+       dev = edrm_kms->dev;
+       if (!dev || !dev->platformdev) {
+               pr_err("invalid device\n");
+               goto error;
+       }
+
+       priv = dev->dev_private;
+       if (!priv) {
+               pr_err("invalid private data\n");
+               goto error;
+       }
+
+       master_priv = edrm_kms->master_dev->dev_private;
+       sde_kms = to_sde_kms(master_priv->kms);
+       rc = sde_power_resource_enable(&master_priv->phandle,
+               sde_kms->core_client, true);
+       if (rc) {
+               pr_err("resource enable failed: %d\n", rc);
+               goto error;
+       }
+
+       /* check bootloader status register */
+       lk_status = edrm_splash_get_lk_status(kms);
+       if (lk_status == SPLASH_STATUS_RUNNING)
+               edrm_kms->lk_running_flag = true;
+       else
+               edrm_kms->lk_running_flag = false;
+
+       /* if early domain is not start, eDRM cannot initialize
+        * display interface and bridge chip.  Need to return err
+        * ToDo:  implement interface and bridge chip startup functions
+        */
+       if (lk_status == SPLASH_STATUS_NOT_START) {
+               rc = -EINVAL;
+               pr_err("LK does not start, eDRM cannot initialize\n");
+               goto power_error;
+       }
+
+       /* only unsecure buffer is support for now */
+       edrm_kms->aspace = sde_kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
+
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+
+       /*
+        * max crtc width is equal to the max mixer width * 2 and max height is
+        * is 4K
+        */
+       dev->mode_config.max_width = sde_kms->catalog->max_sspp_linewidth * 2;
+       dev->mode_config.max_height = 4096;
+
+       /*
+        * Support format modifiers for compression etc.
+        */
+       dev->mode_config.allow_fb_modifiers = true;
+
+       rc = _edrm_kms_drm_obj_init(edrm_kms);
+       if (rc) {
+               pr_err("drm obj init failed: %d\n", rc);
+               goto power_error;
+       }
+
+       /* notify main DRM that eDRM is started */
+       edrm_display_acquire(kms);
+
+       sde_power_resource_enable(&master_priv->phandle,
+                               sde_kms->core_client, false);
+       return 0;
+power_error:
+       sde_power_resource_enable(&master_priv->phandle,
+                       sde_kms->core_client, false);
+error:
+       return rc;
+}
+
+static long edrm_kms_round_pixclk(struct msm_kms *kms, unsigned long rate,
+       struct drm_encoder *encoder)
+{
+       return rate;
+}
+
+static const struct msm_kms_funcs edrm_kms_funcs = {
+       .hw_init         = edrm_kms_hw_init,
+       .postinit        = edrm_kms_postinit,
+       .prepare_fence   = edrm_kms_prepare_fence,
+       .prepare_commit  = edrm_kms_prepare_commit,
+       .commit          = edrm_kms_commit,
+       .complete_commit = edrm_kms_complete_commit,
+       .wait_for_crtc_commit_done = edrm_kms_wait_for_commit_done,
+       .check_modified_format = sde_format_check_modified_format,
+       .get_format      = sde_get_msm_format,
+       .round_pixclk    = edrm_kms_round_pixclk,
+       .destroy         = edrm_kms_destroy,
+       .lastclose       = edrm_kms_lastclose,
+};
+
+struct msm_kms *msm_edrm_kms_init(struct drm_device *dev)
+{
+       struct msm_edrm_kms *edrm_kms;
+       struct drm_minor *minor;
+
+       if (!dev || !dev->dev_private) {
+               pr_err("drm device node invalid\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       minor = drm_minor_acquire(0);
+       if (IS_ERR_OR_NULL(minor))
+               return ERR_PTR(-EINVAL);
+
+       edrm_kms = kzalloc(sizeof(*edrm_kms), GFP_KERNEL);
+       if (!edrm_kms) {
+               drm_minor_release(minor);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       msm_kms_init(&edrm_kms->base, &edrm_kms_funcs);
+       edrm_kms->dev = dev;
+       edrm_kms->master_dev = minor->dev;
+       drm_minor_release(minor);
+
+       return &edrm_kms->base;
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_kms.h b/drivers/gpu/drm/msm/ekms/edrm_kms.h
new file mode 100644 (file)
index 0000000..214c5b8
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _EDRM_KMS_H_
+#define _EDRM_KMS_H_
+
+#include <drm/drmP.h>
+#include "msm_kms.h"
+
+#define MAX_CTRLS_PER_DISPLAY 2
+
+struct msm_edrm_display {
+       int display_id;
+       int ctl_id;
+       int intf_id;
+       int encoder_type;
+       int connector_type;
+       struct drm_display_mode mode;
+       int ctl_off;
+       int lm_off;
+       int plane_cnt;
+};
+
+struct msm_edrm_kms {
+       struct msm_kms base;
+       struct drm_device *dev;
+       struct drm_device *master_dev;
+       struct msm_gem_address_space *aspace;
+
+       struct msm_edrm_display display[MAX_ENCODERS];
+       int display_count;
+
+       int plane_id[MAX_PLANES];
+       int plane_count;
+
+       /* when this flag is set, the next lastclose() will trigger
+        * handoff eDRM resource to main kernel.
+        */
+       bool handoff_flag;
+       bool lk_running_flag;
+};
+
+struct msm_kms *msm_edrm_kms_init(struct drm_device *dev);
+
+#define to_edrm_kms(x) container_of(x, struct msm_edrm_kms, base)
+
+#endif /* _EDRM_KMS_H_ */
diff --git a/drivers/gpu/drm/msm/ekms/edrm_plane.c b/drivers/gpu/drm/msm/ekms/edrm_plane.c
new file mode 100644 (file)
index 0000000..367a092
--- /dev/null
@@ -0,0 +1,280 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "edrm_plane.h"
+#include "edrm_crtc.h"
+#include "sde_kms.h"
+#include "edrm_kms.h"
+
+/* SDE_SSPP_SRC */
+#define SSPP_SRC_SIZE                      0x00
+#define SSPP_SRC_XY                        0x08
+#define SSPP_OUT_SIZE                      0x0c
+#define SSPP_OUT_XY                        0x10
+#define SSPP_SRC0_ADDR                     0x14
+#define SSPP_SRC1_ADDR                     0x18
+#define SSPP_SRC2_ADDR                     0x1C
+#define SSPP_SRC3_ADDR                     0x20
+#define SSPP_SRC_YSTRIDE0                  0x24
+#define SSPP_SRC_YSTRIDE1                  0x28
+#define SSPP_SRC_FORMAT                    0x30
+#define SSPP_SRC_UNPACK_PATTERN            0x34
+#define SSPP_SRC_OP_MODE                   0x38
+#define SSPP_CONSTANT_COLOR                0x3c
+#define PIPE_SW_PIXEL_EXT_C0_REQ           0x108
+#define PIPE_SW_PIXEL_EXT_C1C2_REQ         0x118
+#define PIPE_SW_PIXEL_EXT_C3_REQ           0x128
+#define FLUSH_OFFSET                       0x18
+#define SSPP_SOLID_FILL_FORMAT             0x004237FF
+#define SSPP_RGB888_FORMAT                 0x000237FF
+#define SSPP_RGB_PATTERN                   0x03020001
+
+void edrm_plane_destroy(struct drm_plane *plane)
+{
+       struct edrm_plane *edrm_plane = to_edrm_plane(plane);
+
+       drm_plane_helper_disable(plane);
+       drm_plane_cleanup(plane);
+       kfree(edrm_plane);
+}
+
+int edrm_plane_flush(struct drm_plane *plane)
+{
+       struct edrm_plane *edrm_plane = to_edrm_plane(plane);
+       struct edrm_crtc *edrm_crtc = to_edrm_crtc(plane->state->crtc);
+       u32 sspp_flush_mask_bit[10] = {
+                               0, 1, 2, 18, 3, 4, 5, 19, 11, 12};
+
+       edrm_crtc->sspp_flush_mask |=
+               BIT(sspp_flush_mask_bit[edrm_plane->sspp_cfg_id - 1]);
+       return 0;
+}
+
+static int edrm_plane_atomic_check(struct drm_plane *plane,
+               struct drm_plane_state *state)
+{
+       /* TODO: check plane setting */
+       return 0;
+}
+
+static void edrm_plane_atomic_update(struct drm_plane *plane,
+               struct drm_plane_state *state)
+{
+       struct drm_device *dev = plane->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       struct msm_edrm_kms *edrm_kms;
+       struct msm_drm_private *master_priv;
+       struct sde_kms *master_kms;
+       struct edrm_plane *edrm_plane;
+       u32 img_size, src_xy, dst_xy;
+       u32 ystride0, plane_addr;
+
+       edrm_kms = to_edrm_kms(kms);
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+
+       if (!plane->state->crtc) {
+               pr_err("state crtc is null, skip pipe programming\n");
+               return;
+       }
+       if (!plane->state->fb) {
+               pr_err("state fb is null, skip pipe programming\n");
+               return;
+       }
+       /* Support RGB format only */
+       edrm_plane = to_edrm_plane(plane);
+       img_size = (plane->state->fb->height << 16) | plane->state->fb->width;
+       src_xy = (plane->state->src_x << 16) | plane->state->src_y;
+       dst_xy = (plane->state->crtc_x << 16) | plane->state->crtc_y;
+       ystride0 = (plane->state->fb->width *
+               plane->state->fb->bits_per_pixel/8);
+       plane_addr = msm_framebuffer_iova(plane->state->fb,
+               edrm_plane->aspace, 0);
+       if (!plane_addr) {
+               pr_err("plane update failed to retrieve base addr\n");
+               return;
+       }
+
+       /* rectangle register programming */
+       writel_relaxed(plane_addr, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_SRC0_ADDR);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_SRC_SIZE);
+       writel_relaxed(src_xy, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_SRC_XY);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_OUT_SIZE);
+       writel_relaxed(dst_xy, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_OUT_XY);
+       writel_relaxed(ystride0, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_SRC_YSTRIDE0);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       PIPE_SW_PIXEL_EXT_C0_REQ);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       PIPE_SW_PIXEL_EXT_C1C2_REQ);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       PIPE_SW_PIXEL_EXT_C3_REQ);
+
+       /* RGB888 format */
+       writel_relaxed(SSPP_RGB888_FORMAT, master_kms->mmio +
+               edrm_plane->sspp_offset + SSPP_SRC_FORMAT);
+       writel_relaxed(SSPP_RGB_PATTERN, master_kms->mmio +
+               edrm_plane->sspp_offset + SSPP_SRC_UNPACK_PATTERN);
+}
+
+/* Plane disable should setup the sspp to show a transparent frame
+ * If the pipe still attached with a buffer pointer, the buffer could
+ * be released and cause SMMU fault.  We don't want to change CTL and
+ * LM during eDRM closing because main DRM could be updating CTL and
+ * LM at any moment.  In eDRM lastclose(), it will notify main DRM to
+ * release eDRM display resouse.  The next main DRM commit will clear
+ * the stage setup by eDRM
+ */
+static void edrm_plane_atomic_disable(struct drm_plane *plane,
+               struct drm_plane_state *state)
+{
+       struct drm_device *dev = plane->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       struct msm_edrm_kms *edrm_kms;
+       struct msm_drm_private *master_priv;
+       struct sde_kms *master_kms;
+       struct msm_edrm_display *display;
+       struct edrm_plane *edrm_plane;
+       u32 lm_off, img_size, stride;
+
+       edrm_kms = to_edrm_kms(kms);
+       master_priv = edrm_kms->master_dev->dev_private;
+       master_kms = to_sde_kms(master_priv->kms);
+       dev = edrm_kms->dev;
+       priv = dev->dev_private;
+
+       edrm_plane = to_edrm_plane(plane);
+       display = &edrm_kms->display[edrm_plane->display_id];
+       lm_off = display->lm_off;
+
+       /* setup SSPP */
+       img_size = (display->mode.vdisplay << 16) | display->mode.hdisplay;
+       stride = display->mode.hdisplay * 4;
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+               SSPP_SRC_SIZE);
+       writel_relaxed(0, master_kms->mmio + edrm_plane->sspp_offset
+               + SSPP_SRC_XY);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       SSPP_OUT_SIZE);
+       writel_relaxed(0, master_kms->mmio + edrm_plane->sspp_offset +
+                       SSPP_OUT_XY);
+       writel_relaxed(stride, master_kms->mmio + edrm_plane->sspp_offset +
+                       SSPP_SRC_YSTRIDE0);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       PIPE_SW_PIXEL_EXT_C0_REQ);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+                       PIPE_SW_PIXEL_EXT_C1C2_REQ);
+       writel_relaxed(img_size, master_kms->mmio + edrm_plane->sspp_offset +
+               PIPE_SW_PIXEL_EXT_C3_REQ);
+
+       /* RGB format */
+       writel_relaxed(SSPP_SOLID_FILL_FORMAT, master_kms->mmio +
+               edrm_plane->sspp_offset + SSPP_SRC_FORMAT);
+       writel_relaxed(SSPP_RGB888_FORMAT, master_kms->mmio +
+               edrm_plane->sspp_offset + SSPP_SRC_UNPACK_PATTERN);
+       /* do a solid fill of transparent color */
+       writel_relaxed(0x0, master_kms->mmio + edrm_plane->sspp_offset +
+                       SSPP_CONSTANT_COLOR);
+}
+
+static int edrm_plane_prepare_fb(struct drm_plane *plane,
+               const struct drm_plane_state *new_state)
+{
+       struct drm_framebuffer *fb;
+       struct edrm_plane *edrm_plane;
+
+       if (!plane || !new_state)
+               return -EINVAL;
+
+       if (!new_state->fb)
+               return 0;
+       edrm_plane = to_edrm_plane(plane);
+       fb = new_state->fb;
+       return msm_framebuffer_prepare(fb, edrm_plane->aspace);
+}
+
+static void edrm_plane_cleanup_fb(struct drm_plane *plane,
+               const struct drm_plane_state *old_state)
+{
+       struct drm_framebuffer *fb = old_state ? old_state->fb : NULL;
+       struct edrm_plane *edrm_plane = plane ? to_edrm_plane(plane) : NULL;
+
+       if (!fb || !plane)
+               return;
+
+       msm_framebuffer_cleanup(fb, edrm_plane->aspace);
+}
+
+static const struct drm_plane_funcs edrm_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .destroy = edrm_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,
+};
+
+static const struct drm_plane_helper_funcs edrm_plane_helper_funcs = {
+       .prepare_fb = edrm_plane_prepare_fb,
+       .cleanup_fb = edrm_plane_cleanup_fb,
+       .atomic_check = edrm_plane_atomic_check,
+       .atomic_update = edrm_plane_atomic_update,
+       .atomic_disable = edrm_plane_atomic_disable,
+};
+
+static uint32_t edrm_plane_formats[] = {
+       DRM_FORMAT_ARGB8888,
+};
+
+struct drm_plane *edrm_plane_init(struct drm_device *dev, int pipe)
+{
+       struct msm_drm_private *priv;
+       struct msm_edrm_kms *edrm_kms;
+       struct edrm_plane *edrm_plane;
+       struct drm_plane *plane;
+       int ret;
+
+       edrm_plane = kzalloc(sizeof(*edrm_plane), GFP_KERNEL);
+       if (!edrm_plane) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       plane = &edrm_plane->base;
+
+       ret = drm_universal_plane_init(dev, plane, 0,
+               &edrm_plane_funcs,
+               edrm_plane_formats,
+               ARRAY_SIZE(edrm_plane_formats),
+               DRM_PLANE_TYPE_PRIMARY);
+       if (ret)
+               goto fail;
+
+       drm_plane_helper_add(plane, &edrm_plane_helper_funcs);
+
+       priv = dev->dev_private;
+       edrm_kms = to_edrm_kms(priv->kms);
+
+       edrm_plane->pipe = pipe;
+       edrm_plane->aspace = edrm_kms->aspace;
+
+       return plane;
+fail:
+       return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_plane.h b/drivers/gpu/drm/msm/ekms/edrm_plane.h
new file mode 100644 (file)
index 0000000..1fa70ed
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _EDRM_PLANE_H_
+#define _EDRM_PLANE_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "edrm_kms.h"
+
+struct edrm_plane {
+       struct drm_plane base;
+       struct msm_gem_address_space *aspace;
+       int pipe;
+       int display_id;
+       u32 sspp_offset;
+       u32 sspp_cfg_id;
+       u32 lm_stage;
+};
+
+int edrm_plane_flush(struct drm_plane *plane);
+
+struct drm_plane *edrm_plane_init(struct drm_device *dev, int id);
+
+void edrm_plane_destroy(struct drm_plane *plane);
+
+#define to_edrm_plane(x) container_of(x, struct edrm_plane, base)
+
+#endif /* _EDRM_ENCODER_H_ */
diff --git a/drivers/gpu/drm/msm/ekms/edrm_splash.c b/drivers/gpu/drm/msm/ekms/edrm_splash.c
new file mode 100644 (file)
index 0000000..ec94586
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/of_address.h>
+#include <linux/debugfs.h>
+#include <linux/memblock.h>
+#include <soc/qcom/early_domain.h>
+#include "msm_drv.h"
+#include "sde_kms.h"
+#include "edrm_kms.h"
+#include "sde_splash.h"
+#include "edrm_splash.h"
+
+/* scratch registers */
+#define SCRATCH_REGISTER_0             0x014
+#define SCRATCH_REGISTER_1             0x018
+#define SCRATCH_REGISTER_2             0x01C
+#define SCRATCH_REGISTER_3             0x020
+
+#define SDE_RUNNING_VALUE                      0xC001CAFE
+#define SDE_LK_STOP_VALUE      0xDEADDEAD
+#define SDE_EXIT_VALUE         0xDEADBEEF
+#define SDE_LK_IMMEDIATE_STOP_VALUE    0xFEFEFEFE
+
+/**
+ * edrm_splash_notify_lk_stop_splash.
+ *
+ * Function to stop early splash in LK.
+ */
+void edrm_splash_notify_lk_stop_splash(struct msm_kms *kms)
+{
+       request_early_service_shutdown(EARLY_DISPLAY);
+}
+
+/**
+ * edrm_splash_poll_lk_stop_splash.
+ *
+ * Function to poll for early splash stop in LK.
+ */
+void edrm_splash_poll_lk_stop_splash(struct msm_kms *kms)
+{
+       int i = 0;
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+
+       /* each read may wait up to 10000us, worst case polling is 4 sec */
+       while (i < 400) {
+               /* read LK status from scratch register*/
+               if (!get_early_service_status(EARLY_DISPLAY)) {
+                       edrm_kms->lk_running_flag = false;
+                       break;
+               }
+               usleep_range(8000, 10000);
+               i++;
+       }
+}
+
+/*
+ * Below function will indicate early display exited or not started.
+ */
+int edrm_splash_get_lk_status(struct msm_kms *kms)
+{
+       if (get_early_service_status(EARLY_DISPLAY))
+               return SPLASH_STATUS_RUNNING;
+       else
+               return SPLASH_STATUS_NOT_START;
+}
+
+
+/*
+ * Below function will indicate early display started.
+ */
+void edrm_display_acquire(struct msm_kms *kms)
+{
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+       struct sde_kms *master_kms;
+       struct sde_splash_info *master_sinfo;
+       struct msm_drm_private *master_priv =
+                       edrm_kms->master_dev->dev_private;
+
+       master_kms = to_sde_kms(master_priv->kms);
+       master_sinfo = &master_kms->splash_info;
+       master_sinfo->early_display_enabled = true;
+}
+
+/*
+ * Below function will indicate early display exited or not started.
+ */
+void edrm_display_release(struct msm_kms *kms)
+{
+       struct msm_edrm_kms *edrm_kms = to_edrm_kms(kms);
+       struct sde_kms *master_kms;
+       struct sde_splash_info *master_sinfo;
+       struct msm_drm_private *master_priv =
+                       edrm_kms->master_dev->dev_private;
+
+       master_kms = to_sde_kms(master_priv->kms);
+       master_sinfo = &master_kms->splash_info;
+       master_sinfo->early_display_enabled = false;
+}
diff --git a/drivers/gpu/drm/msm/ekms/edrm_splash.h b/drivers/gpu/drm/msm/ekms/edrm_splash.h
new file mode 100644 (file)
index 0000000..78d3806
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 EDRM_SPLASH_H_
+#define EDRM_SPLASH_H_
+
+#define SPLASH_STATUS_NOT_START 0
+#define SPLASH_STATUS_RUNNING 1
+#define SPLASH_STATUS_STOP    2
+
+/* APIs for early splash handoff functions */
+
+/**
+ * edrm_splash_notify_lk_stop_splash.
+ *
+ * Tell LK to stop display splash.  LK may continue to run until last frame.
+ */
+void edrm_splash_notify_lk_stop_splash(struct msm_kms *kms);
+
+
+/**
+ * edrm_splash_poll_lk_stop_splash.
+ *
+ * Wait unitl LK stop the spash at last frame or it exited the splash app.
+ */
+void edrm_splash_poll_lk_stop_splash(struct msm_kms *kms);
+
+/**
+ * edrm_splash_get_lk_status
+ *
+ * Get early display status to set the status flag.
+ */
+int edrm_splash_get_lk_status(struct msm_kms *kms);
+
+/**
+ * edrm_display_acquire
+ *
+ * Update main DRM that eDRM is active and eDRM display resource is being used.
+ */
+void edrm_display_acquire(struct msm_kms *kms);
+
+/**
+ * edrm_splash_get_lk_status
+ *
+ * Update main DRM that eDRM is active and eDRM display resource no longer
+ * being use.  Main DRM can claim back the resource anytime.
+ */
+void edrm_display_release(struct msm_kms *kms);
+
+#endif
index 341738f..8b5d861 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -258,33 +258,55 @@ static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx,
        return 0;
 }
 
-static inline int sde_hw_ctl_get_splash_mixercfg(const u32 *resv_pipes,
-                                               u32 length)
+static inline void sde_hw_ctl_get_splash_mixer_mask(const u32 *resv_pipes,
+                               u32 length, u32 *mixercfg, u32 *mixercfg_ext)
 {
        int i = 0;
-       u32 mixercfg = 0;
+       u32 mixer_mask = 0;
+       u32 mixer_ext_mask = 0;
 
        for (i = 0; i < length; i++) {
-               /* LK's splash VIG layer always stays on top */
+               /* LK's splash VIG layer always stays on second top */
+               /*  most layerearly HMI RGB layer stays at top most layer */
                switch (resv_pipes[i]) {
                case SSPP_VIG0:
-                       mixercfg |= 0x7 << 0;
+                       mixer_mask |= 0x7 << 0;
+                       mixer_ext_mask |= BIT(0);
                        break;
                case SSPP_VIG1:
-                       mixercfg |= 0x7 << 3;
+                       mixer_mask |= 0x7 << 3;
+                       mixer_ext_mask |= BIT(2);
                        break;
                case SSPP_VIG2:
-                       mixercfg |= 0x7 << 6;
+                       mixer_mask |= 0x7 << 6;
+                       mixer_ext_mask |= BIT(4);
                        break;
                case SSPP_VIG3:
-                       mixercfg |= 0x7 << 26;
+                       mixer_mask |= 0x7 << 26;
+                       mixer_ext_mask |= BIT(6);
+                       break;
+               case SSPP_RGB0:
+                       mixer_mask |= 0x7 << 9;
+                       mixer_ext_mask |= BIT(8);
+                       break;
+               case SSPP_RGB1:
+                       mixer_mask |= 0x7 << 12;
+                       mixer_ext_mask |= BIT(10);
+                       break;
+               case SSPP_RGB2:
+                       mixer_mask |= 0x7 << 15;
+                       mixer_ext_mask |= BIT(12);
+                       break;
+               case SSPP_RGB3:
+                       mixer_mask |= 0x7 << 29;
+                       mixer_ext_mask |= BIT(14);
                        break;
                default:
                        break;
                }
        }
-
-       return mixercfg;
+       *mixercfg = mixer_mask;
+       *mixercfg_ext = mixer_ext_mask;
 }
 
 static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
@@ -346,25 +368,33 @@ static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx,
 {
        struct sde_hw_blk_reg_map *c = &ctx->hw;
        int i;
+       u32 mixercfg = 0;
+       u32 mixercfg_ext = 0;
+       u32 mixer_mask, mixerext_mask;
+       int mixer_id;
 
        for (i = 0; i < ctx->mixer_count; i++) {
-               int mixer_id = ctx->mixer_hw_caps[i].id;
-               u32 mixercfg = 0;
+               mixer_id = ctx->mixer_hw_caps[i].id;
 
                /*
                 * if bootloaer still has early RVC running, mixer status
                 * can't be direcly cleared.
                 */
                if (handoff) {
-                       mixercfg =
-                               sde_hw_ctl_get_splash_mixercfg(resv_pipes,
-                                               resv_pipes_length);
-
-                       mixercfg &= SDE_REG_READ(c, CTL_LAYER(mixer_id));
+                       /*
+                        * if bootloaer still has early display or early RVC
+                        * running,mixer status can't be direcly cleared.
+                        */
+                       mixercfg = SDE_REG_READ(c, CTL_LAYER(mixer_id));
+                       mixercfg_ext = SDE_REG_READ(c,
+                               CTL_LAYER_EXT(mixer_id));
+                       sde_hw_ctl_get_splash_mixer_mask(resv_pipes,
+                               resv_pipes_length, &mixer_mask, &mixerext_mask);
+                       mixercfg &= mixer_mask;
+                       mixercfg_ext &= mixerext_mask;
                }
-
                SDE_REG_WRITE(c, CTL_LAYER(mixer_id), mixercfg);
-               SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), 0);
+               SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), mixercfg_ext);
                SDE_REG_WRITE(c, CTL_LAYER_EXT2(mixer_id), 0);
                SDE_REG_WRITE(c, CTL_LAYER_EXT3(mixer_id), 0);
        }
@@ -376,6 +406,7 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 {
        struct sde_hw_blk_reg_map *c = &ctx->hw;
        u32 mixercfg, mixercfg_ext, mix, ext, mixercfg_ext2;
+       u32 mixer_mask, mixerext_mask;
        int i, j;
        u8 stages;
        int pipes_per_stage;
@@ -402,13 +433,13 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
         * should be updated to kernel's mixer setup.
         */
        if (handoff) {
-               mixercfg =
-                       sde_hw_ctl_get_splash_mixercfg(resv_pipes,
-                                               resv_pipes_length);
-
-               mixercfg &= SDE_REG_READ(c, CTL_LAYER(lm));
+               mixercfg = SDE_REG_READ(c, CTL_LAYER(lm));
+               mixercfg_ext = SDE_REG_READ(c, CTL_LAYER_EXT(lm));
+               sde_hw_ctl_get_splash_mixer_mask(resv_pipes,
+                               resv_pipes_length, &mixer_mask, &mixerext_mask);
+               mixercfg &= mixer_mask;
+               mixercfg_ext &= mixerext_mask;
                mixercfg |= BIT(24);
-               stages--;
        }
 
        for (i = 0; i <= stages; i++) {
index a008ecf..5538c80 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -146,7 +146,7 @@ struct sde_hw_ctl_ops {
        /**
         * Set all blend stages to disabled
         * @ctx       : ctl path ctx pointer
-        * @handoff   : handoff flag
+        * @handoff   : indicate if lk is prepare for handoff
         * @resv_pipes  : reserved pipes in DT
         * @resv_pipes_length:    array size of array reserved_pipes
         */
@@ -158,7 +158,7 @@ struct sde_hw_ctl_ops {
         * @ctx       : ctl path ctx pointer
         * @lm        : layer mixer enumeration
         * @cfg       : blend stage configuration
-        * @handoff   : handoff flag
+        * @handoff   : indicate if lk is prepare for handoff
         * @resv_pipes  : reserved pipes in DT
         * @resv_pipes_length:   array size of array reserved_pipes
         */
index 44a5f8c..2ad6d60 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
  *
@@ -359,13 +359,13 @@ static void sde_kms_prepare_commit(struct msm_kms *kms,
        struct drm_device *dev = sde_kms->dev;
        struct msm_drm_private *priv = dev->dev_private;
 
+       sde_power_resource_enable(&priv->phandle,
+                       sde_kms->core_client, true);
+
        if (sde_kms->splash_info.handoff &&
                sde_kms->splash_info.display_splash_enabled)
                sde_splash_lk_stop_splash(kms, state);
 
-       sde_power_resource_enable(&priv->phandle,
-                       sde_kms->core_client, true);
-
        shd_display_prepare_commit(sde_kms, state);
 }
 
@@ -1508,7 +1508,7 @@ static int sde_kms_hw_init(struct msm_kms *kms)
                        goto power_error;
                }
 
-               rc = sde_splash_parse_reserved_plane_dt(sinfo,
+               rc = sde_splash_parse_reserved_plane_dt(dev, sinfo,
                                                        sde_kms->catalog);
                if (rc)
                        SDE_ERROR("parse reserved plane dt failed: %d\n", rc);
index b0d63ec..2b9a070 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -505,7 +505,8 @@ static inline u32 _sde_splash_parse_sspp_id(struct sde_mdss_cfg *cfg,
        return 0;
 }
 
-int sde_splash_parse_reserved_plane_dt(struct sde_splash_info *splash_info,
+int sde_splash_parse_reserved_plane_dt(struct drm_device *dev,
+                               struct sde_splash_info *splash_info,
                                struct sde_mdss_cfg *cfg)
 {
        struct device_node *parent, *node;
@@ -516,7 +517,8 @@ int sde_splash_parse_reserved_plane_dt(struct sde_splash_info *splash_info,
        if (!splash_info || !cfg)
                return -EINVAL;
 
-       parent = of_find_node_by_path("/qcom,sde-reserved-plane");
+       parent = of_get_child_by_name(dev->dev->of_node,
+                       "qcom,sde-reserved-plane");
        if (!parent)
                return -EINVAL;
 
@@ -761,6 +763,8 @@ bool sde_splash_get_lk_complete_status(struct msm_kms *kms)
        intr = sde_kms->hw_intr;
 
        if (sde_kms->splash_info.handoff &&
+               !sde_kms->splash_info.display_splash_enabled &&
+               !sde_kms->splash_info.early_display_enabled &&
                !_sde_splash_lk_check()) {
                SDE_DEBUG("LK totally exits\n");
                return true;
@@ -940,11 +944,6 @@ int sde_splash_lk_stop_splash(struct msm_kms *kms,
 
        sinfo = &sde_kms->splash_info;
 
-       if (!sinfo) {
-               SDE_ERROR("%s(%d): invalid splash info\n", __func__, __LINE__);
-               return -EINVAL;
-       }
-
        /* Monitor LK's status and tell it to exit. */
        mutex_lock(&sde_splash_lock);
        if (_sde_splash_validate_commit(sde_kms, state) &&
index ee94c34..8332c99 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -56,6 +56,12 @@ struct sde_splash_info {
        /* to indicate LK is totally exited */
        bool lk_is_exited;
 
+       /* flag of early display status */
+       bool early_display_enabled;
+
+       /* flag of early RVC status */
+       bool early_camera_enabled;
+
        /* memory node used for display buffer */
        uint32_t splash_mem_num;
 
@@ -140,8 +146,9 @@ int sde_splash_parse_memory_dt(struct drm_device *dev);
  *
  * Parse reserved plane information from DT for early RVC case.
  */
-int sde_splash_parse_reserved_plane_dt(struct sde_splash_info *splash_info,
-                                       struct sde_mdss_cfg *cfg);
+int sde_splash_parse_reserved_plane_dt(struct drm_device *dev,
+                               struct sde_splash_info *splash_info,
+                               struct sde_mdss_cfg *cfg);
 
 /*
  * sde_splash_query_plane_is_reserved