diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2019-01-25 12:34:46 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2019-01-25 12:34:45 -0800 |
commit | ee0238800a526797285b66599afac3f2da4b3e41 (patch) | |
tree | 601d31829daf410319f99b7a0ca408f83cf4299a /drivers | |
parent | da3b06ed9d040e70501b05329d1d8060a43979f4 (diff) | |
parent | d62b1c14062093cdec0a53451257ab779402633c (diff) |
Merge "drm/msm: Early DRM Driver"
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/msm/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/Makefile | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_connector.c | 123 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_connector.h | 28 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_crtc.c | 263 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_crtc.h | 62 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_drv.c | 439 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_encoder.c | 112 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_encoder.h | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_kms.c | 721 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_kms.h | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_plane.c | 280 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_plane.h | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_splash.c | 109 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/ekms/edrm_splash.h | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_ctl.c | 83 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_ctl.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/sde/sde_kms.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/sde/sde_splash.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/sde/sde_splash.h | 13 |
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, ¶m); + 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 |