From fc6b8eb5d0658740e6d97961d2e6ff4853acf77a Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 17:08:44 +0530 Subject: [PATCH] disp: msm: dp: add support for eDP display Changes to support eDP display detection and bringup through panel power sequencing. Change-Id: Ie20906467c209872296e8b5475cee0ca98a3b232 Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog.c | 29 ++++++++ msm/dp/dp_catalog.h | 1 + msm/dp/dp_display.c | 174 +++++++++++++++++++++++++++++++++++++++++-- msm/dp/dp_display.h | 2 + msm/dp/dp_drm.c | 12 ++- msm/dp/dp_lphw_hpd.h | 2 + msm/dp/dp_parser.c | 9 ++- msm/dp/dp_parser.h | 14 ++++ msm/dp/dp_power.c | 36 ++++++++- msm/dp/dp_power.h | 3 + 10 files changed, 269 insertions(+), 13 deletions(-) diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index 36683b1223ba..e14a10bf8f52 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -11,6 +11,8 @@ #include "dp_catalog.h" #include "dp_reg.h" #include "dp_debug.h" +#include "dp_link.h" +#include "dp_lphw_hpd.h" #define DP_GET_MSB(x) (x >> 8) #define DP_GET_LSB(x) (x & 0xff) @@ -2342,6 +2344,32 @@ static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd) return isr; } +static bool dp_catalog_hpd_wait_for_edp_panel_ready(struct dp_catalog_hpd *hpd) +{ + u32 reg, state; + void __iomem *base; + bool success = true; + u32 const poll_sleep_us = 2000; + u32 const pll_timeout_us = 1000000; + struct dp_catalog_private *catalog; + + catalog = dp_catalog_get_priv(hpd); + + base = catalog->io.dp_aux->io.base; + + reg = DP_DP_HPD_INT_STATUS; + + if (readl_poll_timeout_atomic((base + reg), state, + ((state & DP_HPD_STATE_STATUS_CONNECTED) > 0), + poll_sleep_us, pll_timeout_us)) { + DP_ERR("DP_HPD_STATE_STATUS CONNECTED bit is still low, status=%x\n", state); + + success = false; + } + + return success; +} + static void dp_catalog_audio_init(struct dp_catalog_audio *audio) { struct dp_catalog_private *catalog; @@ -2836,6 +2864,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) struct dp_catalog_hpd hpd = { .config_hpd = dp_catalog_hpd_config_hpd, .get_interrupt = dp_catalog_hpd_get_interrupt, + .wait_for_edp_panel_ready = dp_catalog_hpd_wait_for_edp_panel_ready, }; struct dp_catalog_audio audio = { .init = dp_catalog_audio_init, diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index 8ee97890b92b..5ddfba2d8770 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -108,6 +108,7 @@ struct dp_catalog_ctrl { struct dp_catalog_hpd { void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en); u32 (*get_interrupt)(struct dp_catalog_hpd *hpd); + bool (*wait_for_edp_panel_ready)(struct dp_catalog_hpd *hpd); }; #define HEADER_BYTE_2_BIT 0 diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 844c2174ff95..df1535d0dbf2 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1069,6 +1069,32 @@ error_ctrl: return rc; } +static int dp_display_panel_ready(struct dp_display_private *dp) +{ + int rc = 0; + + if (dp->dp_display.is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, true); + if (rc) { + DP_ERR("Cannot turn edp panel power on"); + return rc; + } + + if (!(dp->catalog->hpd.wait_for_edp_panel_ready(&dp->catalog->hpd))) { + DP_ERR("EDP PANEL is not ready yet, powering off panel\n"); + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); + if (rc) { + DP_ERR("Cannot turn edp panel power off"); + return rc; + } + return -ETIMEDOUT; + } + } + dp->panel->init(dp->panel); + + return 0; +} + static int dp_display_host_ready(struct dp_display_private *dp) { int rc = 0; @@ -1106,7 +1132,6 @@ static int dp_display_host_ready(struct dp_display_private *dp) dp->ctrl->abort(dp->ctrl, false); dp->aux->init(dp->aux, dp->parser->aux_cfg); - dp->panel->init(dp->panel); dp_display_state_add(DP_STATE_READY); /* log this as it results from user action of cable connection */ @@ -1220,6 +1245,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) goto end; } + rc = dp_display_panel_ready(dp); + dp->link->psm_config(dp->link, &dp->panel->link_info, false); dp->debug->psm_enabled = false; @@ -2164,6 +2191,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) struct dp_display_private *dp; struct dp_panel *dp_panel; int rc = 0; + bool shallow_mode = true; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2243,6 +2271,13 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) goto end; } + rc = dp_display_panel_ready(dp); + if (rc) { + dp_display_host_unready(dp); + dp_display_host_deinit(dp); + goto end; + } + if (dp->debug->psm_enabled) { dp->link->psm_config(dp->link, &dp->panel->link_info, false); dp->debug->psm_enabled = false; @@ -2259,7 +2294,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) * and required things. */ rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en, - dp_panel->dsc_en, true); + dp_panel->dsc_en, shallow_mode); if (rc) goto end; @@ -2381,6 +2416,23 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) if (rc) goto end; + /*edp backlight enable and edp pwm enable*/ + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, true); + if (rc) { + DP_ERR("Cannot turn edp backlight power on"); + goto end; + } + + usleep_range(99000, 100000); + + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, true); + if (rc) { + DP_ERR("Cannot turn edp PWM on "); + goto end; + } + } + dp_display_update_dsc_resources(dp, panel, true); dp_display_state_add(DP_STATE_ENABLED); end: @@ -2400,6 +2452,7 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel; + int rc = 0; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2438,6 +2491,14 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) dp_display_stream_post_enable(dp, dp_panel); + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, true); + if (rc) { + DP_ERR("Cannot turn edp backlight power on"); + goto end; + } + } + cancel_delayed_work_sync(&dp->hdcp_cb_work); queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); @@ -2491,6 +2552,14 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) goto end; } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, false); + if (rc) { + DP_ERR("Cannot turn edp backlight power off"); + goto end; + } + } + dp_display_state_add(DP_STATE_HDCP_ABORTED); cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp_display_is_hdcp_enabled(dp) && @@ -2539,7 +2608,7 @@ end: static int dp_display_disable(struct dp_display *dp_display, void *panel) { - int i; + int i, rc = 0; struct dp_display_private *dp = NULL; struct dp_panel *dp_panel = NULL; struct dp_link_hdcp_status *status; @@ -2566,6 +2635,16 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) goto end; } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, false); + if (rc) + DP_ERR("Cannot turn edp backlight power off\n"); + + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, false); + if (rc) + DP_ERR("Cannot turn edp PWM off\n"); + } + dp_display_stream_disable(dp, dp_panel); dp_display_update_dsc_resources(dp, dp_panel, false); @@ -2634,6 +2713,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) struct dp_display_private *dp; struct dp_panel *dp_panel = panel; u32 flags = 0; + int rc = 0; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2671,6 +2751,13 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) dp_display_state_add(DP_STATE_SRC_PWRDN); } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); + if (rc) { + DP_ERR("Cannot turn edp panel power off\n"); + goto end; + } + } dp_display_state_remove(DP_STATE_ENABLED); dp->aux->state = DP_STATE_CTRL_POWERED_OFF; @@ -2689,18 +2776,25 @@ end: static int dp_display_validate_link_clock(struct dp_display_private *dp, struct drm_display_mode *mode, struct dp_display_mode dp_mode) { - u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; + u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0, lane_count = 0; bool dsc_en; - int rate; + int rate = 0; dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false; mode_bpp = dsc_en ? DSC_BPP(dp_mode.timing.comp_info.dsc_info.config) : dp_mode.timing.bpp; + if (dp->dp_display.is_edp) { + rate = dp->panel->link_info.rate; + lane_count = dp->panel->link_info.num_lanes; + } else { + rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); + lane_count = dp->link->link_params.lane_count; + } + mode_rate_khz = mode->clock * mode_bpp; - rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); - supported_rate_khz = dp->link->link_params.lane_count * rate * 8; + supported_rate_khz = lane_count * rate * 8; if (mode_rate_khz > supported_rate_khz) { DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n", @@ -3607,6 +3701,71 @@ static int dp_display_mst_get_fixed_topology_display_type( return 0; } +static int dp_display_edp_detect(struct dp_display *dp_display) +{ + struct device *dev; + struct dp_display_private *dp; + int rc = 0; + + dp = container_of(dp_display, struct dp_display_private, dp_display); + dev = &dp->pdev->dev; + + if (dp->dp_display.is_sst_connected) + return rc; + + mutex_lock(&dp->session_lock); + + rc = dp_display_host_init(dp); + if (rc) { + DP_ERR("Host init Failed"); + goto end; + } + + dp->dp_display.is_sst_connected = true; + dp->hpd->hpd_high = true; + dp->hpd->alt_mode_cfg_done = true; + + dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); + + rc = dp_display_host_ready(dp); + if (rc) { + dp_display_state_show("[ready failed]"); + dp_display_host_deinit(dp); + goto end; + } + + rc = dp_display_panel_ready(dp); + if (rc) { + dp_display_host_unready(dp); + dp_display_host_deinit(dp); + goto end; + } + + dp->link->psm_config(dp->link, &dp->panel->link_info, false); + dp->debug->psm_enabled = false; + + rc = dp->panel->read_sink_caps(dp->panel, + dp->dp_display.base_connector, dp->hpd->multi_func); + + if (rc == -ETIMEDOUT || rc == -ENOTCONN) + goto end; + + dp_display_state_remove(DP_STATE_ABORTED); + dp_display_state_add(DP_STATE_CONFIGURED); + dp_display_state_add(DP_STATE_CONNECTED); + + dp->link->process_request(dp->link); + dp->panel->handle_sink_request(dp->panel); + + dp_display_state_add(DP_STATE_CONNECT_NOTIFIED); + dp_display_state_remove(DP_STATE_DISCONNECT_NOTIFIED); + + dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); +end: + mutex_unlock(&dp->session_lock); + return rc; +} + static int dp_display_probe(struct platform_device *pdev) { int rc = 0; @@ -3697,6 +3856,7 @@ static int dp_display_probe(struct platform_device *pdev) dp_display_mst_get_fixed_topology_display_type; g_dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; + g_dp_display->edp_detect = dp_display_edp_detect; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index ae2285becc60..60eab0f4ee31 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -12,6 +12,7 @@ #include #include "dp_panel.h" +#include "dp_parser.h" #define DP_MST_SIM_MAX_PORTS 8 @@ -138,6 +139,7 @@ struct dp_display { const char **display_type); int (*mst_get_fixed_topology_display_type)(struct dp_display *dp_display, u32 strm_id, const char **display_type); + int (*edp_detect)(struct dp_display *dp_display); }; #if IS_ENABLED(CONFIG_DRM_MSM_DP) diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 95d2a834ebe7..6e054af5dc3d 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -476,11 +476,13 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn, { enum drm_connector_status status = connector_status_unknown; struct msm_display_info info; + struct dp_display *dp_disp; int rc; if (!conn || !display) return status; + dp_disp = display; /* get display dp_info */ memset(&info, 0x0, sizeof(info)); rc = dp_connector_get_info(conn, &info, display); @@ -489,12 +491,18 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn, return connector_status_disconnected; } - if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) + if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) { status = (info.is_connected ? connector_status_connected : connector_status_disconnected); - else + } else { status = connector_status_connected; + rc = dp_disp->edp_detect(dp_disp); + if (rc) { + DP_ERR("error in turning on panel power sequence rc:%d\n", rc); + return connector_status_unknown; + } + } conn->display_info.width_mm = info.width_mm; conn->display_info.height_mm = info.height_mm; diff --git a/msm/dp/dp_lphw_hpd.h b/msm/dp/dp_lphw_hpd.h index 9779331bd295..095302e2da84 100644 --- a/msm/dp/dp_lphw_hpd.h +++ b/msm/dp/dp_lphw_hpd.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ @@ -12,6 +13,7 @@ #define DP_IRQ_HPD_INT_STATUS BIT(1) #define DP_HPD_REPLUG_INT_STATUS BIT(2) #define DP_HPD_UNPLUG_INT_STATUS BIT(3) +#define DP_HPD_STATE_STATUS_CONNECTED BIT(30) /** * dp_lphw_hpd_get() - configure and get the DisplayPlot HPD module data diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 87adcd2e1088..1a18d69ed070 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -258,23 +258,26 @@ static int dp_parser_gpio(struct dp_parser *parser) struct device *dev = &parser->pdev->dev; struct device_node *of_node = dev->of_node; struct dss_module_power *mp = &parser->mp[DP_CORE_PM]; - static const char * const dp_gpios[] = { + static const char * const dp_gpios[DP_GPIO_MAX] = { "qcom,aux-en-gpio", "qcom,aux-sel-gpio", "qcom,usbplug-cc-gpio", + "qcom,edp-vcc-en-gpio", + "qcom,edp-backlight-pwr-gpio", + "qcom,edp-pwm-en-gpio", + "qcom,edp-backlight-en-gpio", }; if (of_find_property(of_node, "qcom,dp-hpd-gpio", NULL)) { parser->no_aux_switch = true; parser->lphw_hpd = of_find_property(of_node, "qcom,dp-low-power-hw-hpd", NULL); - return 0; } if (of_find_property(of_node, "qcom,dp-gpio-aux-switch", NULL)) parser->gpio_aux_switch = true; mp->gpio_config = devm_kzalloc(dev, - sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL); + sizeof(struct dss_gpio) * DP_GPIO_MAX, GFP_KERNEL); if (!mp->gpio_config) return -ENOMEM; diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index cdeb85f422c2..4404930d255a 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -26,6 +26,20 @@ enum dp_pm_type { DP_MAX_PM }; +enum dp_pin_states { + DP_GPIO_AUX_ENABLE, + DP_GPIO_AUX_SEL, + DP_GPIO_USBPLUG_CC, + DP_GPIO_CMN_MAX = DP_GPIO_USBPLUG_CC, + DP_GPIO_EDP_VCC_EN, + DP_GPIO_EDP_MIN = DP_GPIO_EDP_VCC_EN, + DP_GPIO_EDP_BACKLIGHT_PWR, + DP_GPIO_EDP_PWM, + DP_GPIO_EDP_BACKLIGHT_EN, + DP_GPIO_EDP_MAX, + DP_GPIO_MAX = DP_GPIO_EDP_MAX, +}; + static inline const char *dp_parser_pm_name(enum dp_pm_type module) { switch (module) { diff --git a/msm/dp/dp_power.c b/msm/dp/dp_power.c index ebafae298b86..dd32941f84a7 100644 --- a/msm/dp/dp_power.c +++ b/msm/dp/dp_power.c @@ -388,6 +388,7 @@ static int dp_power_request_gpios(struct dp_power_private *power) struct dss_module_power *mp; static const char * const gpio_names[] = { "aux_enable", "aux_sel", "usbplug_cc", + "edp_vcc_enable", "edp_backlight_pwr", "edp_pwm_en", "edp_backlight_en", }; if (!power) { @@ -410,6 +411,7 @@ static int dp_power_request_gpios(struct dp_power_private *power) } } } + return 0; error: for (i = 0; i < ARRAY_SIZE(gpio_names); i++) { @@ -432,7 +434,7 @@ static void dp_power_set_gpio(struct dp_power_private *power, bool flip) struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; struct dss_gpio *config = mp->gpio_config; - for (i = 0; i < mp->num_gpio; i++) { + for (i = 0; i <= DP_GPIO_CMN_MAX; i++) { if (dp_power_find_gpio(config->gpio_name, "aux-sel")) config->value = flip; @@ -689,6 +691,37 @@ exit: return rc; } +static int dp_power_edp_panel_set_gpio(struct dp_power *dp_power, + enum dp_pin_states pin_state, bool enable) +{ + int rc = 0; + struct dp_power_private *power; + struct dss_module_power *mp; + struct dss_gpio *config; + + if (!dp_power) { + DP_ERR("invalid power data\n"); + return -EINVAL; + } + + power = container_of(dp_power, struct dp_power_private, dp_power); + + mp = &power->parser->mp[DP_CORE_PM]; + config = mp->gpio_config; + + if (config == NULL) + return -EINVAL; + + if ((pin_state >= DP_GPIO_EDP_MIN) && (pin_state < DP_GPIO_EDP_MAX)) { + gpio_direction_output(config[pin_state].gpio, enable); + } else { + pr_err(" Invalid GPIO call\n"); + return -EINVAL; + } + + return rc; +} + struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll) { int rc = 0; @@ -720,6 +753,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll) dp_power->clk_get_rate = dp_power_clk_get_rate; dp_power->power_client_init = dp_power_client_init; dp_power->power_client_deinit = dp_power_client_deinit; + dp_power->edp_panel_set_gpio = dp_power_edp_panel_set_gpio; return dp_power; error: diff --git a/msm/dp/dp_power.h b/msm/dp/dp_power.h index 7a0302356de1..d605c2801fbc 100644 --- a/msm/dp/dp_power.h +++ b/msm/dp/dp_power.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -34,6 +35,8 @@ struct dp_power { struct sde_power_handle *phandle, struct drm_device *drm_dev); void (*power_client_deinit)(struct dp_power *power); + int (*edp_panel_set_gpio)(struct dp_power *power, enum dp_pin_states pin_state, + bool enable); }; /**