diff options
| author | Ajay Singh Parmar <aparmar@codeaurora.org> | 2016-05-16 17:55:52 -0700 |
|---|---|---|
| committer | Dhaval Patel <pdhaval@codeaurora.org> | 2016-08-01 11:58:13 -0700 |
| commit | 7b778d492026b62b96dbfc145a70b8af50dfab41 (patch) | |
| tree | 377afdc5b6a60defa25a72daca515ef9fde97268 /drivers/gpu | |
| parent | 17440d54b1e74f9655317f3c74205f2c217f27ec (diff) | |
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>
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_display.c | 1989 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_display.h | 407 |
2 files changed, 2396 insertions, 0 deletions
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 000000000000..68e3b52c8605 --- /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 000000000000..df15bb84270f --- /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_ */ |
