summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/msm/Kconfig10
-rw-r--r--drivers/gpu/drm/msm/Makefile9
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_connector.c123
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_connector.h28
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_crtc.c263
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_crtc.h62
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_drv.c439
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_encoder.c112
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_encoder.h50
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_kms.c721
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_kms.h58
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_plane.c280
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_plane.h40
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_splash.c109
-rw-r--r--drivers/gpu/drm/msm/ekms/edrm_splash.h59
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.c83
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.h6
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.c15
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.h13
20 files changed, 2445 insertions, 45 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index cf95a8b9b68d..d2240c53edd3 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -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
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 6edbca08536f..dd721cd8b0e6 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -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
index 000000000000..8beaa598aa5e
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_connector.c
@@ -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
index 000000000000..4bd6deb7b6d0
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_connector.h
@@ -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
index 000000000000..b39ec94dc69c
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_crtc.c
@@ -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
index 000000000000..761a6a97e5b2
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_crtc.h
@@ -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
index 000000000000..69b8c01e59d4
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_drv.c
@@ -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
index 000000000000..0cee78c73f50
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_encoder.c
@@ -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
index 000000000000..eeb91d659535
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_encoder.h
@@ -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
index 000000000000..3aa45776a914
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_kms.c
@@ -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
index 000000000000..214c5b85e614
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_kms.h
@@ -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
index 000000000000..367a0925104d
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_plane.c
@@ -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
index 000000000000..1fa70ed65424
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_plane.h
@@ -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
index 000000000000..ec94586b1261
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_splash.c
@@ -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
index 000000000000..78d3806a7adf
--- /dev/null
+++ b/drivers/gpu/drm/msm/ekms/edrm_splash.h
@@ -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
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 341738f624db..8b5d8614f7ec 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -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++) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index a008ecf4a11d..5538c807692c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -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
*/
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 44a5f8c4535b..2ad6d608d9be 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -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);
diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c
index b0d63ec4c64c..2b9a070db0fe 100644
--- a/drivers/gpu/drm/msm/sde/sde_splash.c
+++ b/drivers/gpu/drm/msm/sde/sde_splash.c
@@ -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) &&
diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h
index ee94c348e25b..8332c99c6f1b 100644
--- a/drivers/gpu/drm/msm/sde/sde_splash.h
+++ b/drivers/gpu/drm/msm/sde/sde_splash.h
@@ -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