drm/msm/dsi-staging: add dsi display driver
Add dsi display driver. Configure all internal DSI modules.
Manage power sequence for internal modules like controller
and phy. Configure lanes, modes etc.
Change-Id: I8edc918c4858f16d32af9373fc4626066b20d2e5
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
new file mode 100644
index 0000000..68e3b52
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -0,0 +1,1989 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#define pr_fmt(fmt) "msm-dsi-display:[%s] " fmt, __func__
+
+#include <linux/list.h>
+#include <linux/of.h>
+
+#include "msm_drv.h"
+#include "dsi_display.h"
+#include "dsi_panel.h"
+#include "dsi_ctrl.h"
+#include "dsi_ctrl_hw.h"
+#include "dsi_drm.h"
+
+#define to_dsi_display(x) container_of(x, struct dsi_display, host)
+
+static DEFINE_MUTEX(dsi_display_list_lock);
+static LIST_HEAD(dsi_display_list);
+
+static struct dsi_display *main_display;
+
+static int dsi_display_ctrl_power_on(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ /* Sequence does not matter for split dsi usecases */
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl)
+ continue;
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_ON);
+ if (rc) {
+ pr_err("[%s] Failed to set power state, rc=%d\n",
+ ctrl->ctrl->name, rc);
+ goto error;
+ }
+ }
+
+ return rc;
+error:
+ for (i = i - 1; i >= 0; i--) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl)
+ continue;
+ (void)dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ }
+ return rc;
+}
+
+static int dsi_display_ctrl_power_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ /* Sequence does not matter for split dsi usecases */
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl)
+ continue;
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ if (rc) {
+ pr_err("[%s] Failed to power off, rc=%d\n",
+ ctrl->ctrl->name, rc);
+ goto error;
+ }
+ }
+error:
+ return rc;
+}
+
+static int dsi_display_phy_power_on(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ /* Sequence does not matter for split dsi usecases */
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl)
+ continue;
+
+ rc = dsi_phy_set_power_state(ctrl->phy, true);
+ if (rc) {
+ pr_err("[%s] Failed to set power state, rc=%d\n",
+ ctrl->phy->name, rc);
+ goto error;
+ }
+ }
+
+ return rc;
+error:
+ for (i = i - 1; i >= 0; i--) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->phy)
+ continue;
+ (void)dsi_phy_set_power_state(ctrl->phy, false);
+ }
+ return rc;
+}
+
+static int dsi_display_phy_power_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ /* Sequence does not matter for split dsi usecases */
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->phy)
+ continue;
+
+ rc = dsi_phy_set_power_state(ctrl->phy, false);
+ if (rc) {
+ pr_err("[%s] Failed to power off, rc=%d\n",
+ ctrl->ctrl->name, rc);
+ goto error;
+ }
+ }
+error:
+ return rc;
+}
+
+static int dsi_display_ctrl_core_clk_on(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+
+ rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn on clocks, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ /* Turn on rest of the controllers */
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_CORE_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn on clock, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
+error:
+ return rc;
+}
+
+static int dsi_display_ctrl_link_clk_on(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+
+ rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl,
+ &display->clock_info.src_clks);
+ if (rc) {
+ pr_err("[%s] failed to set source clocks for master, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_LINK_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn on clocks, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ /* Turn on rest of the controllers */
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_clock_source(ctrl->ctrl,
+ &display->clock_info.src_clks);
+ if (rc) {
+ pr_err("[%s] failed to set source clocks, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_LINK_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn on clock, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_ctrl_set_power_state(m_ctrl->ctrl,
+ DSI_CTRL_POWER_CORE_CLK_ON);
+error:
+ return rc;
+}
+
+static int dsi_display_ctrl_core_clk_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn off clock, rc=%d\n",
+ display->name, rc);
+ }
+ }
+
+ rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
+ if (rc)
+ pr_err("[%s] failed to turn off clocks, rc=%d\n",
+ display->name, rc);
+
+ return rc;
+}
+
+static int dsi_display_ctrl_link_clk_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_CORE_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to turn off clock, rc=%d\n",
+ display->name, rc);
+ }
+ }
+ rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
+ if (rc)
+ pr_err("[%s] failed to turn off clocks, rc=%d\n",
+ display->name, rc);
+ return rc;
+}
+
+static int dsi_display_ctrl_init(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ for (i = 0 ; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_host_init(ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] failed to init host_%d, rc=%d\n",
+ display->name, i, rc);
+ goto error_host_deinit;
+ }
+ }
+
+ return 0;
+error_host_deinit:
+ for (i = i - 1; i >= 0; i--) {
+ ctrl = &display->ctrl[i];
+ (void)dsi_ctrl_host_deinit(ctrl->ctrl);
+ }
+ return rc;
+}
+
+static int dsi_display_ctrl_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ for (i = 0 ; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_host_deinit(ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] failed to deinit host_%d, rc=%d\n",
+ display->name, i, rc);
+ }
+ }
+
+ return rc;
+}
+
+static int dsi_display_cmd_engine_enable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+
+ return rc;
+error_disable_master:
+ (void)dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+error:
+ return rc;
+}
+
+static int dsi_display_cmd_engine_disable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_OFF);
+ if (rc)
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ }
+
+ rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_display_ctrl_host_enable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_ctrl_set_host_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable host engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_host_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable sl host engine, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+
+ return rc;
+error_disable_master:
+ (void)dsi_ctrl_set_host_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+error:
+ return rc;
+}
+
+static int dsi_display_ctrl_host_disable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_host_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_OFF);
+ if (rc)
+ pr_err("[%s] failed to disable host engine, rc=%d\n",
+ display->name, rc);
+ }
+
+ rc = dsi_ctrl_set_host_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+ if (rc) {
+ pr_err("[%s] failed to disable host engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_display_vid_engine_enable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->video_master_idx];
+
+ rc = dsi_ctrl_set_vid_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable vid engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_vid_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable vid engine, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+
+ return rc;
+error_disable_master:
+ (void)dsi_ctrl_set_vid_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+error:
+ return rc;
+}
+
+static int dsi_display_vid_engine_disable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->video_master_idx];
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_vid_engine_state(ctrl->ctrl,
+ DSI_CTRL_ENGINE_OFF);
+ if (rc)
+ pr_err("[%s] failed to disable vid engine, rc=%d\n",
+ display->name, rc);
+ }
+
+ rc = dsi_ctrl_set_vid_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+ if (rc)
+ pr_err("[%s] failed to disable mvid engine, rc=%d\n",
+ display->name, rc);
+
+ return rc;
+}
+
+static int dsi_display_phy_enable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+ enum dsi_phy_pll_source m_src = DSI_PLL_SOURCE_STANDALONE;
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+ if (display->ctrl_count > 1)
+ m_src = DSI_PLL_SOURCE_NATIVE;
+
+ rc = dsi_phy_enable(m_ctrl->phy,
+ &display->config,
+ m_src,
+ true);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI PHY, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_enable(ctrl->phy,
+ &display->config,
+ DSI_PLL_SOURCE_NON_NATIVE,
+ true);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI PHY, rc=%d\n",
+ display->name, rc);
+ goto error_disable_master;
+ }
+ }
+
+ return rc;
+
+error_disable_master:
+ (void)dsi_phy_disable(m_ctrl->phy);
+error:
+ return rc;
+}
+
+static int dsi_display_phy_disable(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->clk_master_idx];
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_disable(ctrl->phy);
+ if (rc)
+ pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
+ display->name, rc);
+ }
+
+ rc = dsi_phy_disable(m_ctrl->phy);
+ if (rc)
+ pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
+ display->name, rc);
+
+ return rc;
+}
+
+static int dsi_display_wake_up(struct dsi_display *display)
+{
+ return 0;
+}
+
+static int dsi_display_broadcast_cmd(struct dsi_display *display,
+ const struct mipi_dsi_msg *msg)
+{
+ int rc = 0;
+ u32 flags, m_flags;
+ struct dsi_display_ctrl *ctrl, *m_ctrl;
+ int i;
+
+ m_flags = (DSI_CTRL_CMD_BROADCAST | DSI_CTRL_CMD_BROADCAST_MASTER |
+ DSI_CTRL_CMD_DEFER_TRIGGER | DSI_CTRL_CMD_FIFO_STORE);
+ flags = (DSI_CTRL_CMD_BROADCAST | DSI_CTRL_CMD_DEFER_TRIGGER |
+ DSI_CTRL_CMD_FIFO_STORE);
+
+ /*
+ * 1. Setup commands in FIFO
+ * 2. Trigger commands
+ */
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ rc = dsi_ctrl_cmd_transfer(m_ctrl->ctrl, msg, m_flags);
+ if (rc) {
+ pr_err("[%s] cmd transfer failed on master,rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (ctrl == m_ctrl)
+ continue;
+
+ rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, msg, flags);
+ if (rc) {
+ pr_err("[%s] cmd transfer failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_ctrl_cmd_tx_trigger(ctrl->ctrl,
+ DSI_CTRL_CMD_BROADCAST);
+ if (rc) {
+ pr_err("[%s] cmd trigger failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+ }
+
+ rc = dsi_ctrl_cmd_tx_trigger(m_ctrl->ctrl,
+ (DSI_CTRL_CMD_BROADCAST_MASTER |
+ DSI_CTRL_CMD_BROADCAST));
+ if (rc) {
+ pr_err("[%s] cmd trigger failed for master, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_display_phy_sw_reset(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_ctrl_phy_sw_reset(m_ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] failed to reset phy, rc=%d\n", display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_phy_sw_reset(ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] failed to reset phy, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *dsi)
+{
+ return 0;
+}
+
+static int dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *dsi)
+{
+ return 0;
+}
+
+static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct dsi_display *display = to_dsi_display(host);
+
+ int rc = 0;
+
+ if (!host || !msg) {
+ pr_err("Invalid params\n");
+ return 0;
+ }
+
+ rc = dsi_display_wake_up(display);
+ if (rc) {
+ pr_err("[%s] failed to wake up display, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_display_cmd_engine_enable(display);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ if (display->ctrl_count > 1) {
+ rc = dsi_display_broadcast_cmd(display, msg);
+ if (rc) {
+ pr_err("[%s] cmd broadcast failed, rc=%d\n",
+ display->name, rc);
+ goto error_disable_cmd_engine;
+ }
+ } else {
+ rc = dsi_ctrl_cmd_transfer(display->ctrl[0].ctrl, msg,
+ DSI_CTRL_CMD_FIFO_STORE);
+ if (rc) {
+ pr_err("[%s] cmd transfer failed, rc=%d\n",
+ display->name, rc);
+ goto error_disable_cmd_engine;
+ }
+ }
+error_disable_cmd_engine:
+ (void)dsi_display_cmd_engine_disable(display);
+error:
+ return rc;
+}
+
+
+static struct mipi_dsi_host_ops dsi_host_ops = {
+ .attach = dsi_host_attach,
+ .detach = dsi_host_detach,
+ .transfer = dsi_host_transfer,
+};
+
+static int dsi_display_mipi_host_init(struct dsi_display *display)
+{
+ int rc = 0;
+ struct mipi_dsi_host *host = &display->host;
+
+ host->dev = &display->pdev->dev;
+ host->ops = &dsi_host_ops;
+
+ rc = mipi_dsi_host_register(host);
+ if (rc) {
+ pr_err("[%s] failed to register mipi dsi host, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+static int dsi_display_mipi_host_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+ struct mipi_dsi_host *host = &display->host;
+
+ mipi_dsi_host_unregister(host);
+
+ host->dev = NULL;
+ host->ops = NULL;
+
+ return rc;
+}
+
+static int dsi_display_clocks_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+ struct dsi_clk_link_set *src = &display->clock_info.src_clks;
+ struct dsi_clk_link_set *mux = &display->clock_info.mux_clks;
+ struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks;
+
+ if (src->byte_clk) {
+ devm_clk_put(&display->pdev->dev, src->byte_clk);
+ src->byte_clk = NULL;
+ }
+
+ if (src->pixel_clk) {
+ devm_clk_put(&display->pdev->dev, src->pixel_clk);
+ src->pixel_clk = NULL;
+ }
+
+ if (mux->byte_clk) {
+ devm_clk_put(&display->pdev->dev, mux->byte_clk);
+ mux->byte_clk = NULL;
+ }
+
+ if (mux->pixel_clk) {
+ devm_clk_put(&display->pdev->dev, mux->pixel_clk);
+ mux->pixel_clk = NULL;
+ }
+
+ if (shadow->byte_clk) {
+ devm_clk_put(&display->pdev->dev, shadow->byte_clk);
+ shadow->byte_clk = NULL;
+ }
+
+ if (shadow->pixel_clk) {
+ devm_clk_put(&display->pdev->dev, shadow->pixel_clk);
+ shadow->pixel_clk = NULL;
+ }
+
+ return rc;
+}
+
+static int dsi_display_clocks_init(struct dsi_display *display)
+{
+ int rc = 0;
+ struct dsi_clk_link_set *src = &display->clock_info.src_clks;
+ struct dsi_clk_link_set *mux = &display->clock_info.mux_clks;
+ struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks;
+
+ src->byte_clk = devm_clk_get(&display->pdev->dev, "src_byte_clk");
+ if (IS_ERR_OR_NULL(src->byte_clk)) {
+ rc = PTR_ERR(src->byte_clk);
+ pr_err("failed to get src_byte_clk, rc=%d\n", rc);
+ goto error;
+ }
+
+ src->pixel_clk = devm_clk_get(&display->pdev->dev, "src_pixel_clk");
+ if (IS_ERR_OR_NULL(src->pixel_clk)) {
+ rc = PTR_ERR(src->pixel_clk);
+ pr_err("failed to get src_pixel_clk, rc=%d\n", rc);
+ goto error;
+ }
+
+ mux->byte_clk = devm_clk_get(&display->pdev->dev, "mux_byte_clk");
+ if (IS_ERR_OR_NULL(mux->byte_clk)) {
+ rc = PTR_ERR(mux->byte_clk);
+ pr_err("failed to get mux_byte_clk, rc=%d\n", rc);
+ /*
+ * Skip getting rest of clocks since one failed. This is a
+ * non-critical failure since these clocks are requied only for
+ * dynamic refresh use cases.
+ */
+ rc = 0;
+ goto done;
+ };
+
+ mux->pixel_clk = devm_clk_get(&display->pdev->dev, "mux_pixel_clk");
+ if (IS_ERR_OR_NULL(mux->pixel_clk)) {
+ rc = PTR_ERR(mux->pixel_clk);
+ pr_err("failed to get mux_pixel_clk, rc=%d\n", rc);
+ /*
+ * Skip getting rest of clocks since one failed. This is a
+ * non-critical failure since these clocks are requied only for
+ * dynamic refresh use cases.
+ */
+ rc = 0;
+ goto done;
+ };
+
+ shadow->byte_clk = devm_clk_get(&display->pdev->dev, "shadow_byte_clk");
+ if (IS_ERR_OR_NULL(shadow->byte_clk)) {
+ rc = PTR_ERR(shadow->byte_clk);
+ pr_err("failed to get shadow_byte_clk, rc=%d\n", rc);
+ /*
+ * Skip getting rest of clocks since one failed. This is a
+ * non-critical failure since these clocks are requied only for
+ * dynamic refresh use cases.
+ */
+ rc = 0;
+ goto done;
+ };
+
+ shadow->pixel_clk = devm_clk_get(&display->pdev->dev,
+ "shadow_pixel_clk");
+ if (IS_ERR_OR_NULL(shadow->pixel_clk)) {
+ rc = PTR_ERR(shadow->pixel_clk);
+ pr_err("failed to get shadow_pixel_clk, rc=%d\n", rc);
+ /*
+ * Skip getting rest of clocks since one failed. This is a
+ * non-critical failure since these clocks are requied only for
+ * dynamic refresh use cases.
+ */
+ rc = 0;
+ goto done;
+ };
+
+done:
+ return 0;
+error:
+ (void)dsi_display_clocks_deinit(display);
+ return rc;
+}
+
+static int dsi_display_parse_lane_map(struct dsi_display *display)
+{
+ int rc = 0;
+
+ display->lane_map.physical_lane0 = DSI_LOGICAL_LANE_0;
+ display->lane_map.physical_lane1 = DSI_LOGICAL_LANE_1;
+ display->lane_map.physical_lane2 = DSI_LOGICAL_LANE_2;
+ display->lane_map.physical_lane3 = DSI_LOGICAL_LANE_3;
+ return rc;
+}
+
+static int dsi_display_parse_dt(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ u32 phy_count = 0;
+ struct device_node *of_node;
+
+ /* Parse controllers */
+ for (i = 0; i < MAX_DSI_CTRLS_PER_DISPLAY; i++) {
+ of_node = of_parse_phandle(display->pdev->dev.of_node,
+ "qcom,dsi-ctrl", i);
+ if (!of_node) {
+ if (!i) {
+ pr_err("No controllers present\n");
+ return -ENODEV;
+ }
+ break;
+ }
+
+ display->ctrl[i].ctrl_of_node = of_node;
+ display->ctrl_count++;
+ }
+
+ /* Parse Phys */
+ for (i = 0; i < MAX_DSI_CTRLS_PER_DISPLAY; i++) {
+ of_node = of_parse_phandle(display->pdev->dev.of_node,
+ "qcom,dsi-phy", i);
+ if (!of_node) {
+ if (!i) {
+ pr_err("No PHY devices present\n");
+ rc = -ENODEV;
+ goto error;
+ }
+ break;
+ }
+
+ display->ctrl[i].phy_of_node = of_node;
+ phy_count++;
+ }
+
+ if (phy_count != display->ctrl_count) {
+ pr_err("Number of controllers does not match PHYs\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ of_node = of_parse_phandle(display->pdev->dev.of_node,
+ "qcom,dsi-panel", 0);
+ if (!of_node) {
+ pr_err("No Panel device present\n");
+ rc = -ENODEV;
+ goto error;
+ } else {
+ display->panel_of = of_node;
+ }
+
+ rc = dsi_display_parse_lane_map(display);
+ if (rc) {
+ pr_err("Lane map not found, rc=%d\n", rc);
+ goto error;
+ }
+error:
+ return rc;
+}
+
+static int dsi_display_res_init(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ ctrl->ctrl = dsi_ctrl_get(ctrl->ctrl_of_node);
+ if (IS_ERR_OR_NULL(ctrl->ctrl)) {
+ rc = PTR_ERR(ctrl->ctrl);
+ pr_err("failed to get dsi controller, rc=%d\n", rc);
+ ctrl->ctrl = NULL;
+ goto error_ctrl_put;
+ }
+
+ ctrl->phy = dsi_phy_get(ctrl->phy_of_node);
+ if (IS_ERR_OR_NULL(ctrl->phy)) {
+ rc = PTR_ERR(ctrl->phy);
+ pr_err("failed to get phy controller, rc=%d\n", rc);
+ dsi_ctrl_put(ctrl->ctrl);
+ ctrl->phy = NULL;
+ goto error_ctrl_put;
+ }
+ }
+
+ display->panel = dsi_panel_get(&display->pdev->dev, display->panel_of);
+ if (IS_ERR_OR_NULL(display->panel)) {
+ rc = PTR_ERR(display->panel);
+ pr_err("failed to get panel, rc=%d\n", rc);
+ display->panel = NULL;
+ goto error_ctrl_put;
+ }
+
+ rc = dsi_display_clocks_init(display);
+ if (rc) {
+ pr_err("Failed to parse clock data, rc=%d\n", rc);
+ goto error_ctrl_put;
+ }
+
+ return 0;
+error_ctrl_put:
+ for (i = i - 1; i >= 0; i--) {
+ ctrl = &display->ctrl[i];
+ dsi_ctrl_put(ctrl->ctrl);
+ dsi_phy_put(ctrl->phy);
+ }
+ return rc;
+}
+
+static int dsi_display_res_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ rc = dsi_display_clocks_deinit(display);
+ if (rc)
+ pr_err("clocks deinit failed, rc=%d\n", rc);
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ dsi_phy_put(ctrl->phy);
+ dsi_ctrl_put(ctrl->ctrl);
+ }
+
+ return rc;
+}
+
+static int dsi_display_validate_mode_set(struct dsi_display *display,
+ struct dsi_display_mode *mode,
+ u32 flags)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ /*
+ * To set a mode:
+ * 1. Controllers should be turned off.
+ * 2. Link clocks should be off.
+ * 3. Phy should be disabled.
+ */
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if ((ctrl->power_state > DSI_CTRL_POWER_VREG_ON) ||
+ (ctrl->phy_enabled)) {
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_display_set_mode_sub(struct dsi_display *display,
+ struct dsi_display_mode *mode,
+ u32 flags)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ rc = dsi_panel_get_host_cfg_for_mode(display->panel,
+ mode,
+ &display->config);
+ if (rc) {
+ pr_err("[%s] failed to get host config for mode, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ memcpy(&display->config.lane_map, &display->lane_map,
+ sizeof(display->lane_map));
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_update_host_config(ctrl->ctrl, &display->config);
+ if (rc) {
+ pr_err("[%s] failed to update ctrl config, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ }
+error:
+ return rc;
+}
+
+int dsi_display_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct dsi_display *display;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("pdev not found\n");
+ return -ENODEV;
+ }
+
+ display = devm_kzalloc(&pdev->dev, sizeof(*display), GFP_KERNEL);
+ if (!display)
+ return -ENOMEM;
+
+ display->name = of_get_property(pdev->dev.of_node, "label", NULL);
+
+ display->is_active = of_property_read_bool(pdev->dev.of_node,
+ "qcom,dsi-display-active");
+
+ display->display_type = of_get_property(pdev->dev.of_node,
+ "qcom,display-type", NULL);
+ if (!display->display_type)
+ display->display_type = "unknown";
+
+ mutex_init(&display->display_lock);
+
+ display->pdev = pdev;
+ platform_set_drvdata(pdev, display);
+ mutex_lock(&dsi_display_list_lock);
+ list_add(&display->list, &dsi_display_list);
+ mutex_unlock(&dsi_display_list_lock);
+ if (display->is_active)
+ main_display = display;
+ return rc;
+}
+
+int dsi_display_dev_remove(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct dsi_display *display;
+ struct dsi_display *pos, *tmp;
+
+ if (!pdev) {
+ pr_err("Invalid device\n");
+ return -EINVAL;
+ }
+
+ display = platform_get_drvdata(pdev);
+
+ mutex_lock(&dsi_display_list_lock);
+ list_for_each_entry_safe(pos, tmp, &dsi_display_list, list) {
+ if (pos == display) {
+ list_del(&display->list);
+ break;
+ }
+ }
+ mutex_unlock(&dsi_display_list_lock);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, display);
+ return rc;
+}
+
+u32 dsi_display_get_num_of_displays(void)
+{
+ u32 count = 0;
+ struct dsi_display *display;
+
+ mutex_lock(&dsi_display_list_lock);
+
+ list_for_each_entry(display, &dsi_display_list, list) {
+ count++;
+ }
+
+ mutex_unlock(&dsi_display_list_lock);
+ return count;
+}
+
+struct dsi_display *dsi_display_get_display_by_index(u32 index)
+{
+ struct dsi_display *display = NULL;
+ struct dsi_display *pos;
+ int i = 0;
+
+ mutex_lock(&dsi_display_list_lock);
+
+ list_for_each_entry(pos, &dsi_display_list, list) {
+ if (i == index) {
+ display = pos;
+ break;
+ }
+ i++;
+ }
+
+ mutex_unlock(&dsi_display_list_lock);
+ return display;
+}
+
+struct dsi_display *dsi_display_get_display_by_name(const char *name)
+{
+ struct dsi_display *display = NULL, *pos;
+
+ mutex_lock(&dsi_display_list_lock);
+
+ list_for_each_entry(pos, &dsi_display_list, list) {
+ if (!strcmp(name, pos->name))
+ display = pos;
+ }
+
+ mutex_unlock(&dsi_display_list_lock);
+
+ return display;
+}
+
+void dsi_display_set_active_state(struct dsi_display *display, bool is_active)
+{
+ mutex_lock(&display->display_lock);
+ display->is_active = is_active;
+ mutex_unlock(&display->display_lock);
+}
+
+bool dsi_display_is_active(struct dsi_display *display)
+{
+ bool is_active;
+
+ mutex_lock(&display->display_lock);
+ is_active = display->is_active;
+ mutex_unlock(&display->display_lock);
+
+ return is_active;
+}
+
+int dsi_display_dev_init(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_display_parse_dt(display);
+ if (rc) {
+ pr_err("[%s] failed to parse dt, rc=%d\n", display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_display_res_init(display);
+ if (rc) {
+ pr_err("[%s] failed to initialize resources, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_dev_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_display_res_deinit(display);
+ if (rc)
+ pr_err("[%s] failed to deinitialize resource, rc=%d\n",
+ display->name, rc);
+
+ mutex_unlock(&display->display_lock);
+
+ return rc;
+}
+
+int dsi_display_bind(struct dsi_display *display, struct drm_device *dev)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *display_ctrl;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ display_ctrl = &display->ctrl[i];
+
+ rc = dsi_ctrl_drv_init(display_ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] Failed to initialize ctrl[%d], rc=%d\n",
+ display->name, i, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_phy_drv_init(display_ctrl->phy);
+ if (rc) {
+ pr_err("[%s] Failed to initialize phy[%d], rc=%d\n",
+ display->name, i, rc);
+ (void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
+ goto error_ctrl_deinit;
+ }
+ }
+
+ rc = dsi_display_mipi_host_init(display);
+ if (rc) {
+ pr_err("[%s] Failed to initialize mipi host, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_panel_drv_init(display->panel, &display->host);
+ if (rc) {
+ pr_err("[%s] Failed to initialize panel driver, rc=%d\n",
+ display->name, rc);
+ goto error_host_deinit;
+ }
+
+ rc = dsi_panel_get_mode_count(display->panel, &display->num_of_modes);
+ if (rc) {
+ pr_err("[%s] Failed to get mode count, rc=%d\n",
+ display->name, rc);
+ goto error_panel_deinit;
+ }
+
+ display->drm_dev = dev;
+ goto error;
+
+error_panel_deinit:
+ (void)dsi_panel_drv_deinit(display->panel);
+error_host_deinit:
+ (void)dsi_display_mipi_host_deinit(display);
+error_ctrl_deinit:
+ for (i = i - 1; i >= 0; i--) {
+ display_ctrl = &display->ctrl[i];
+ (void)dsi_phy_drv_deinit(display_ctrl->phy);
+ (void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
+ }
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_unbind(struct dsi_display *display)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *display_ctrl;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_panel_drv_deinit(display->panel);
+ if (rc)
+ pr_err("[%s] failed to deinit panel driver, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_mipi_host_deinit(display);
+ if (rc)
+ pr_err("[%s] failed to deinit mipi hosts, rc=%d\n",
+ display->name,
+ rc);
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ display_ctrl = &display->ctrl[i];
+
+ rc = dsi_phy_drv_deinit(display_ctrl->phy);
+ if (rc)
+ pr_err("[%s] failed to deinit phy%d driver, rc=%d\n",
+ display->name, i, rc);
+
+ rc = dsi_ctrl_drv_deinit(display_ctrl->ctrl);
+ if (rc)
+ pr_err("[%s] failed to deinit ctrl%d driver, rc=%d\n",
+ display->name, i, rc);
+ }
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_drm_init(struct dsi_display *display, struct drm_encoder *enc)
+{
+ int rc = 0;
+ struct dsi_bridge *bridge;
+ struct dsi_connector *connector;
+ struct msm_drm_private *priv = NULL;
+
+ if (!display || !enc) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+ priv = display->drm_dev->dev_private;
+
+ if (!priv) {
+ pr_err("Private data is not present\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (display->connector || display->bridge) {
+ pr_err("display is already initialize\n");
+ goto error;
+ }
+
+ bridge = dsi_drm_bridge_init(display, display->drm_dev, enc);
+ if (IS_ERR_OR_NULL(bridge)) {
+ rc = PTR_ERR(bridge);
+ pr_err("[%s] brige init failed, rc=%d\n", display->name, rc);
+ goto error;
+ }
+
+ connector = dsi_drm_connector_init(display, display->drm_dev, bridge);
+ if (IS_ERR_OR_NULL(connector)) {
+ rc = PTR_ERR(connector);
+ pr_err("[%s] connector init failed, rc=%d\n", display->name,
+ rc);
+ goto error_bridge_deinit;
+ }
+
+ display->connector = connector;
+ priv->connectors[priv->num_connectors++] = &connector->base;
+ display->bridge = bridge;
+ priv->bridges[priv->num_bridges++] = &bridge->base;
+
+ goto error;
+error_bridge_deinit:
+ dsi_drm_bridge_cleanup(bridge);
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_drm_deinit(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ dsi_drm_connector_cleanup(display->connector);
+ dsi_drm_bridge_cleanup(display->bridge);
+ display->connector = NULL;
+ display->bridge = NULL;
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_get_info(struct dsi_display *display,
+ struct dsi_display_info *info)
+{
+ int rc = 0;
+ int i;
+ struct dsi_panel_phy_props phy_props;
+
+ if (!display || !info) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+ rc = dsi_panel_get_phy_props(display->panel, &phy_props);
+ if (rc) {
+ pr_err("[%s] failed to get panel phy props, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ info->type = display->type;
+
+ /* TODO: do not access dsi_ctrl structure */
+ info->num_of_h_tiles = display->ctrl_count;
+ for (i = 0; i < info->num_of_h_tiles; i++)
+ info->h_tile_ids[i] = display->ctrl[i].ctrl->index;
+
+ info->is_hot_pluggable = false;
+ info->is_edid_supported = false;
+
+ info->width_mm = phy_props.panel_width_mm;
+ info->height_mm = phy_props.panel_height_mm;
+ strlcpy(info->display_type, display->display_type,
+ sizeof(display->display_type));
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_get_modes(struct dsi_display *display,
+ struct dsi_display_mode *modes,
+ u32 *count)
+{
+ int rc = 0;
+ int i;
+
+ if (!display || !count) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ if (!modes) {
+ *count = display->num_of_modes;
+ goto error;
+ }
+
+ for (i = 0; i < *count; i++) {
+ rc = dsi_panel_get_mode(display->panel, i, modes);
+ if (rc) {
+ pr_err("[%s] failed to get mode from panel\n",
+ display->name);
+ goto error;
+ }
+ if (display->ctrl_count > 1) {
+ modes->timing.h_active *= display->ctrl_count;
+ modes->timing.h_front_porch *= display->ctrl_count;
+ modes->timing.h_sync_width *= display->ctrl_count;
+ modes->timing.h_back_porch *= display->ctrl_count;
+ modes->timing.h_skew *= display->ctrl_count;
+ modes->pixel_clk_khz *= display->ctrl_count;
+ }
+
+ modes++;
+ }
+
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_validate_mode(struct dsi_display *display,
+ struct dsi_display_mode *mode)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+ struct dsi_display_mode adj_mode;
+
+ if (!display || !mode) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ adj_mode = *mode;
+ if (display->ctrl_count > 1) {
+ adj_mode.timing.h_active /= display->ctrl_count;
+ adj_mode.timing.h_front_porch /= display->ctrl_count;
+ adj_mode.timing.h_sync_width /= display->ctrl_count;
+ adj_mode.timing.h_back_porch /= display->ctrl_count;
+ adj_mode.timing.h_skew /= display->ctrl_count;
+ adj_mode.pixel_clk_khz /= display->ctrl_count;
+ }
+
+ rc = dsi_panel_validate_mode(display->panel, &adj_mode);
+ if (rc) {
+ pr_err("[%s] panel mode validation failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_validate_timing(ctrl->ctrl, &adj_mode.timing);
+ if (rc) {
+ pr_err("[%s] ctrl mode validation failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_phy_validate_mode(ctrl->phy, &adj_mode.timing);
+ if (rc) {
+ pr_err("[%s] phy mode validation failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+ }
+
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_set_mode(struct dsi_display *display,
+ struct dsi_display_mode *mode,
+ u32 flags)
+{
+ int rc = 0;
+ struct dsi_display_mode adj_mode;
+
+ if (!display || !mode) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ adj_mode = *mode;
+ if (display->ctrl_count > 1) {
+ adj_mode.timing.h_active /= display->ctrl_count;
+ adj_mode.timing.h_front_porch /= display->ctrl_count;
+ adj_mode.timing.h_sync_width /= display->ctrl_count;
+ adj_mode.timing.h_back_porch /= display->ctrl_count;
+ adj_mode.timing.h_skew /= display->ctrl_count;
+ adj_mode.pixel_clk_khz /= display->ctrl_count;
+ }
+
+ rc = dsi_display_validate_mode_set(display, &adj_mode, flags);
+ if (rc) {
+ pr_err("[%s] mode cannot be set\n", display->name);
+ goto error;
+ }
+
+ rc = dsi_display_set_mode_sub(display, &adj_mode, flags);
+ if (rc) {
+ pr_err("[%s] failed to set mode\n", display->name);
+ goto error;
+ }
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_set_tpg_state(struct dsi_display *display, bool enable)
+{
+ int rc = 0;
+ int i;
+ struct dsi_display_ctrl *ctrl;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_set_tpg_state(ctrl->ctrl, enable);
+ if (rc) {
+ pr_err("[%s] failed to set tpg state for host_%d\n",
+ display->name, i);
+ goto error;
+ }
+ }
+
+ display->is_tpg_enabled = enable;
+error:
+ return rc;
+}
+
+int dsi_display_prepare(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_panel_pre_prepare(display->panel);
+ if (rc) {
+ pr_err("[%s] panel pre-prepare failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = dsi_display_ctrl_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
+ display->name, rc);
+ goto error_panel_post_unprep;
+ }
+
+ rc = dsi_display_phy_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi phy, rc = %d\n",
+ display->name, rc);
+ goto error_ctrl_pwr_off;
+ }
+
+ rc = dsi_display_ctrl_core_clk_on(display);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
+ display->name, rc);
+ goto error_phy_pwr_off;
+ }
+
+ rc = dsi_display_phy_sw_reset(display);
+ if (rc) {
+ pr_err("[%s] failed to reset phy, rc=%d\n", display->name, rc);
+ goto error_ctrl_clk_off;
+ }
+
+ rc = dsi_display_phy_enable(display);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI PHY, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_clk_off;
+ }
+
+ rc = dsi_display_ctrl_init(display);
+ if (rc) {
+ pr_err("[%s] failed to setup DSI controller, rc=%d\n",
+ display->name, rc);
+ goto error_phy_disable;
+ }
+
+ rc = dsi_display_ctrl_link_clk_on(display);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI link clocks, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_display_ctrl_host_enable(display);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI host, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_link_off;
+ }
+
+ rc = dsi_panel_prepare(display->panel);
+ if (rc) {
+ pr_err("[%s] panel prepare failed, rc=%d\n", display->name, rc);
+ goto error_host_engine_off;
+ }
+
+ goto error;
+
+error_host_engine_off:
+ (void)dsi_display_ctrl_host_disable(display);
+error_ctrl_link_off:
+ (void)dsi_display_ctrl_link_clk_off(display);
+error_ctrl_deinit:
+ (void)dsi_display_ctrl_deinit(display);
+error_phy_disable:
+ (void)dsi_display_phy_disable(display);
+error_ctrl_clk_off:
+ (void)dsi_display_ctrl_core_clk_off(display);
+error_phy_pwr_off:
+ (void)dsi_display_phy_power_off(display);
+error_ctrl_pwr_off:
+ (void)dsi_display_ctrl_power_off(display);
+error_panel_post_unprep:
+ (void)dsi_panel_post_unprepare(display->panel);
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_enable(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_panel_enable(display->panel);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI panel, rc=%d\n",
+ display->name, rc);
+ goto error_disable_vid_engine;
+ }
+
+ rc = dsi_display_vid_engine_enable(display);
+ if (rc) {
+ pr_err("[%s] failed to enable DSI video engine, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ goto error;
+
+error_disable_vid_engine:
+ (void)dsi_display_vid_engine_disable(display);
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_post_enable(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_panel_post_enable(display->panel);
+ if (rc)
+ pr_err("[%s] panel post-enable failed, rc=%d\n",
+ display->name, rc);
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_pre_disable(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_panel_pre_disable(display->panel);
+ if (rc)
+ pr_err("[%s] panel pre-disable failed, rc=%d\n",
+ display->name, rc);
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_disable(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_display_wake_up(display);
+ if (rc)
+ pr_err("[%s] display wake up failed, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_panel_disable(display->panel);
+ if (rc)
+ pr_err("[%s] failed to disable DSI panel, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_vid_engine_disable(display);
+ if (rc)
+ pr_err("[%s] failed to disable video engine, rc=%d\n",
+ display->name, rc);
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int dsi_display_unprepare(struct dsi_display *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+
+ rc = dsi_display_wake_up(display);
+ if (rc)
+ pr_err("[%s] display wake up failed, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_panel_unprepare(display->panel);
+ if (rc)
+ pr_err("[%s] panel unprepare failed, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_host_disable(display);
+ if (rc)
+ pr_err("[%s] failed to disable DSI host, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_link_clk_off(display);
+ if (rc)
+ pr_err("[%s] failed to disable Link clocks, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_deinit(display);
+ if (rc)
+ pr_err("[%s] failed to deinit controller, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_phy_disable(display);
+ if (rc)
+ pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_core_clk_off(display);
+ if (rc)
+ pr_err("[%s] failed to disable DSI clocks, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_phy_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power off PHY, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power DSI vregs, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_panel_post_unprepare(display->panel);
+ if (rc)
+ pr_err("[%s] panel post-unprepare failed, rc=%d\n",
+ display->name, rc);
+
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
new file mode 100644
index 0000000..df15bb8
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2015-2016, 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 _DSI_DISPLAY_H_
+#define _DSI_DISPLAY_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/of_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+#include "dsi_defs.h"
+#include "dsi_ctrl.h"
+#include "dsi_phy.h"
+#include "dsi_panel.h"
+
+#define MAX_DSI_CTRLS_PER_DISPLAY 2
+
+/**
+ * enum dsi_display_type - enumerates DSI display types
+ * @DSI_DISPLAY_SINGLE: A panel connected on a single DSI interface.
+ * @DSI_DISPLAY_EXT_BRIDGE: A bridge is connected between panel and DSI host.
+ * It utilizes a single DSI interface.
+ * @DSI_DISPLAY_SPLIT: A panel that utilizes more than one DSI
+ * interfaces.
+ * @DSI_DISPLAY_SPLIT_EXT_BRIDGE: A bridge is present between panel and DSI
+ * host. It utilizes more than one DSI interface.
+ */
+enum dsi_display_type {
+ DSI_DISPLAY_SINGLE = 0,
+ DSI_DISPLAY_EXT_BRIDGE,
+ DSI_DISPLAY_SPLIT,
+ DSI_DISPLAY_SPLIT_EXT_BRIDGE,
+ DSI_DISPLAY_MAX,
+};
+
+/**
+ * struct dsi_display_info - defines dsi display properties
+ * @display_type: Display type as defined by device tree.
+ * @type: Type of panel connected to DSI interface.
+ * @num_of_h_tiles: In case of split panels, number of h tiles indicates the
+ * number of dsi interfaces used. For single DSI panels this
+ * is set to 1. This will be set for horizontally split
+ * panels.
+ * @h_tile_ids: The DSI instance ID for each tile.
+ * @is_hot_pluggable: Can panel be hot plugged.
+ * @is_connected: Is panel connected.
+ * @is_edid_supported: Does panel support reading EDID information.
+ * @width_mm: Physical width of panel in millimeters.
+ * @height_mm: Physical height of panel in millimeters.
+ */
+struct dsi_display_info {
+ char display_type[20];
+ enum dsi_display_type type;
+
+ /* Split DSI properties */
+ bool h_tiled;
+ u32 num_of_h_tiles;
+ u32 h_tile_ids[MAX_DSI_CTRLS_PER_DISPLAY];
+
+ /* HPD */
+ bool is_hot_pluggable;
+ bool is_connected;
+ bool is_edid_supported;
+
+ /* Physical properties */
+ u32 width_mm;
+ u32 height_mm;
+};
+
+/**
+ * struct dsi_display_ctrl - dsi ctrl/phy information for the display
+ * @ctrl: Handle to the DSI controller device.
+ * @ctrl_of_node: pHandle to the DSI controller device.
+ * @dsi_ctrl_idx: DSI controller instance id.
+ * @power_state: Current power state of the DSI controller.
+ * @cmd_engine_enabled: Command engine status.
+ * @video_engine_enabled: Video engine status.
+ * @ulps_enabled: ULPS status for the controller.
+ * @clamps_enabled: Clamps status for the controller.
+ * @phy: Handle to the DSI PHY device.
+ * @phy_of_node: pHandle to the DSI PHY device.
+ * @phy_enabled: PHY power status.
+ */
+struct dsi_display_ctrl {
+ /* controller info */
+ struct dsi_ctrl *ctrl;
+ struct device_node *ctrl_of_node;
+ u32 dsi_ctrl_idx;
+
+ enum dsi_power_state power_state;
+ bool cmd_engine_enabled;
+ bool video_engine_enabled;
+ bool ulps_enabled;
+ bool clamps_enabled;
+
+ /* phy info */
+ struct msm_dsi_phy *phy;
+ struct device_node *phy_of_node;
+
+ bool phy_enabled;
+};
+
+/**
+ * struct dsi_display_clk_info - dsi display clock source information
+ * @src_clks: Source clocks for DSI display.
+ * @mux_clks: Mux clocks used for DFPS.
+ * @shadow_clks: Used for DFPS.
+ */
+struct dsi_display_clk_info {
+ struct dsi_clk_link_set src_clks;
+ struct dsi_clk_link_set mux_clks;
+ struct dsi_clk_link_set shadow_clks;
+};
+
+/**
+ * struct dsi_display - dsi display information
+ * @pdev: Pointer to platform device.
+ * @drm_dev: DRM device associated with the display.
+ * @name: Name of the display.
+ * @display_type: Display type as defined in device tree.
+ * @list: List pointer.
+ * @is_active: Is display active.
+ * @display_lock: Mutex for dsi_display interface.
+ * @ctrl_count: Number of DSI interfaces required by panel.
+ * @ctrl: Controller information for DSI display.
+ * @panel: Handle to DSI panel.
+ * @panel_of: pHandle to DSI panel.
+ * @type: DSI display type.
+ * @clk_master_idx: The master controller for controlling clocks. This is an
+ * index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array.
+ * @cmd_master_idx: The master controller for sending DSI commands to panel.
+ * @video_master_idx: The master controller for enabling video engine.
+ * @clock_info: Clock sourcing for DSI display.
+ * @lane_map: Lane mapping between DSI host and Panel.
+ * @num_of_modes: Number of modes supported by display.
+ * @is_tpg_enabled: TPG state.
+ * @host: DRM MIPI DSI Host.
+ * @connector: Pointer to DRM connector object.
+ * @bridge: Pointer to DRM bridge object.
+ */
+struct dsi_display {
+ struct platform_device *pdev;
+ struct drm_device *drm_dev;
+
+ const char *name;
+ const char *display_type;
+ struct list_head list;
+ bool is_active;
+ struct mutex display_lock;
+
+ u32 ctrl_count;
+ struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY];
+
+ /* panel info */
+ struct dsi_panel *panel;
+ struct device_node *panel_of;
+
+ enum dsi_display_type type;
+ u32 clk_master_idx;
+ u32 cmd_master_idx;
+ u32 video_master_idx;
+
+ struct dsi_display_clk_info clock_info;
+ struct dsi_host_config config;
+ struct dsi_lane_mapping lane_map;
+ u32 num_of_modes;
+ bool is_tpg_enabled;
+
+ struct mipi_dsi_host host;
+ struct dsi_connector *connector;
+ struct dsi_bridge *bridge;
+};
+
+int dsi_display_dev_probe(struct platform_device *pdev);
+int dsi_display_dev_remove(struct platform_device *pdev);
+
+/**
+ * dsi_display_get_num_of_displays() - returns number of display devices
+ * supported.
+ *
+ * Return: number of displays.
+ */
+u32 dsi_display_get_num_of_displays(void);
+
+/**
+ * dsi_display_get_display_by_index()- finds display by index
+ * @index: index of the display.
+ *
+ * Return: handle to the display or error code.
+ */
+struct dsi_display *dsi_display_get_display_by_index(u32 index);
+
+/**
+ * dsi_display_get_display_by_name()- finds display by name
+ * @index: name of the display.
+ *
+ * Return: handle to the display or error code.
+ */
+struct dsi_display *dsi_display_get_display_by_name(const char *name);
+
+/**
+ * dsi_display_set_active_state() - sets the state of the display
+ * @display: Handle to display.
+ * @is_active: state
+ */
+void dsi_display_set_active_state(struct dsi_display *display, bool is_active);
+
+/**
+ * dsi_display_is_active() - returns the state of the display
+ * @display: Handle to the display.
+ *
+ * Return: state.
+ */
+bool dsi_display_is_active(struct dsi_display *display);
+
+/**
+ * dsi_display_dev_init() - Initializes the display device
+ * @display: Handle to the display.
+ *
+ * Initialization will acquire references to the resources required for the
+ * display hardware to function.
+ *
+ * Return: error code.
+ */
+int dsi_display_dev_init(struct dsi_display *display);
+
+/**
+ * dsi_display_dev_deinit() - Desinitializes the display device
+ * @display: Handle to the display.
+ *
+ * All the resources acquired during device init will be released.
+ *
+ * Return: error code.
+ */
+int dsi_display_dev_deinit(struct dsi_display *display);
+
+/**
+ * dsi_display_bind() - Binds the display device to the DRM device
+ * @display: Handle to the display.
+ * @dev: Pointer to the DRM device.
+ *
+ * Return: error code.
+ */
+int dsi_display_bind(struct dsi_display *display, struct drm_device *dev);
+
+/**
+ * dsi_display_unbind() - Unbinds the display device from the DRM device
+ * @display: Handle to the display.
+ *
+ * Return: error code.
+ */
+int dsi_display_unbind(struct dsi_display *display);
+
+/**
+ * dsi_display_drm_init() - initializes DRM objects for the display device.
+ * @display: Handle to the display.
+ * @encoder: Pointer to the encoder object which is connected to the
+ * display.
+ *
+ * Return: error code.
+ */
+int dsi_display_drm_init(struct dsi_display *display, struct drm_encoder *enc);
+
+/**
+ * dsi_display_drm_deinit() - destroys DRM objects assosciated with the display
+ * @display: Handle to the display.
+ *
+ * Return: error code.
+ */
+int dsi_display_drm_deinit(struct dsi_display *display);
+
+/**
+ * dsi_display_get_info() - returns the display properties
+ * @display: Handle to the display.
+ * @info: Pointer to the structure where info is stored.
+ *
+ * Return: error code.
+ */
+int dsi_display_get_info(struct dsi_display *display,
+ struct dsi_display_info *info);
+
+/**
+ * dsi_display_get_modes() - get modes supported by display
+ * @display: Handle to display.
+ * @modes; Pointer to array of modes. Memory allocated should be
+ * big enough to store (count * struct dsi_display_mode)
+ * elements. If modes pointer is NULL, number of modes will
+ * be stored in the memory pointed to by count.
+ * @count: If modes is NULL, number of modes will be stored. If
+ * not, mode information will be copied (number of modes
+ * copied will be equal to *count).
+ *
+ * Return: error code.
+ */
+int dsi_display_get_modes(struct dsi_display *display,
+ struct dsi_display_mode *modes,
+ u32 *count);
+
+/**
+ * dsi_display_validate_mode() - validates if mode is supported by display
+ * @display: Handle to display.
+ * @mode: Mode to be validated.
+ *
+ * Return: 0 if supported or error code.
+ */
+int dsi_display_validate_mode(struct dsi_display *display,
+ struct dsi_display_mode *mode);
+
+/**
+ * dsi_display_set_mode() - Set mode on the display.
+ * @display: Handle to display.
+ * @mode: mode to be set.
+ * @flags: Modifier flags.
+ *
+ * Return: error code.
+ */
+int dsi_display_set_mode(struct dsi_display *display,
+ struct dsi_display_mode *mode,
+ u32 flags);
+
+/**
+ * dsi_display_prepare() - prepare display
+ * @display: Handle to display.
+ *
+ * Prepare will perform power up sequences for the host and panel hardware.
+ * Power and clock resources might be turned on (depending on the panel mode).
+ * The video engine is not enabled.
+ *
+ * Return: error code.
+ */
+int dsi_display_prepare(struct dsi_display *display);
+
+/**
+ * dsi_display_enable() - enable display
+ * @display: Handle to display.
+ *
+ * Enable will turn on the host engine and the panel. At the end of the enable
+ * function, Host and panel hardware are ready to accept pixel data from
+ * upstream.
+ *
+ * Return: error code.
+ */
+int dsi_display_enable(struct dsi_display *display);
+
+/**
+ * dsi_display_post_enable() - perform post enable operations.
+ * @display: Handle to display.
+ *
+ * Some panels might require some commands to be sent after pixel data
+ * transmission has started. Such commands are sent as part of the post_enable
+ * function.
+ *
+ * Return: error code.
+ */
+int dsi_display_post_enable(struct dsi_display *display);
+
+/**
+ * dsi_display_pre_disable() - perform pre disable operations.
+ * @display: Handle to display.
+ *
+ * If a panel requires commands to be sent before pixel data transmission is
+ * stopped, those can be sent as part of pre_disable.
+ *
+ * Return: error code.
+ */
+int dsi_display_pre_disable(struct dsi_display *display);
+
+/**
+ * dsi_display_disable() - disable panel and host hardware.
+ * @display: Handle to display.
+ *
+ * Disable host and panel hardware and pixel data transmission can not continue.
+ *
+ * Return: error code.
+ */
+int dsi_display_disable(struct dsi_display *display);
+
+/**
+ * dsi_display_unprepare() - power off display hardware.
+ * @display: Handle to display.
+ *
+ * Host and panel hardware is turned off. Panel will be in reset state at the
+ * end of the function.
+ *
+ * Return: error code.
+ */
+int dsi_display_unprepare(struct dsi_display *display);
+
+int dsi_display_set_tpg_state(struct dsi_display *display, bool enable);
+
+int dsi_display_clock_gate(struct dsi_display *display, bool enable);
+int dsi_dispaly_static_frame(struct dsi_display *display, bool enable);
+
+#endif /* _DSI_DISPLAY_H_ */