diff --git a/msm/dsi/dsi_catalog.c b/msm/dsi/dsi_catalog.c index 7d0434365d..349961a187 100644 --- a/msm/dsi/dsi_catalog.c +++ b/msm/dsi/dsi_catalog.c @@ -192,6 +192,14 @@ static void dsi_catalog_phy_2_0_init(struct dsi_phy_hw *phy) dsi_phy_hw_calculate_timing_params; phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v2_0; phy->ops.clamp_ctrl = dsi_phy_hw_v2_0_clamp_ctrl; + phy->ops.dyn_refresh_ops.dyn_refresh_config = + dsi_phy_hw_v2_0_dyn_refresh_config; + phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay = + dsi_phy_hw_v2_0_dyn_refresh_pipe_delay; + phy->ops.dyn_refresh_ops.dyn_refresh_helper = + dsi_phy_hw_v2_0_dyn_refresh_helper; + phy->ops.dyn_refresh_ops.cache_phy_timings = + dsi_phy_hw_v2_0_cache_phy_timings; } /** diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index 2e946ab37b..79c84cfc3a 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef _DSI_CATALOG_H_ @@ -80,6 +80,13 @@ void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy); int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg, u32 *timing_val, u32 size); void dsi_phy_hw_v2_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable); +void dsi_phy_hw_v2_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset); +void dsi_phy_hw_v2_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master); +void dsi_phy_hw_v2_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay); +int dsi_phy_hw_v2_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size); /* Definitions for 10nm PHY hardware driver */ void dsi_phy_hw_v3_0_regulator_enable(struct dsi_phy_hw *phy, diff --git a/msm/dsi/dsi_clk.h b/msm/dsi/dsi_clk.h index ff6bfb908f..02765c6e3f 100644 --- a/msm/dsi/dsi_clk.h +++ b/msm/dsi/dsi_clk.h @@ -106,11 +106,13 @@ struct dsi_link_lp_clk_info { /** * struct link_clk_freq - Clock frequency information for Link clocks * @byte_clk_rate: Frequency of DSI byte_clk in KHz. + * @byte_intf_clk_rate: Frequency of DSI byte_intf_clk in KHz. * @pixel_clk_rate: Frequency of DSI pixel_clk in KHz. * @esc_clk_rate: Frequency of DSI escape clock in KHz. */ struct link_clk_freq { u32 byte_clk_rate; + u32 byte_intf_clk_rate; u32 pix_clk_rate; u32 esc_clk_rate; }; @@ -292,10 +294,12 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index); * dsi_clk_set_byte_clk_rate() - set frequency for byte clock * @client: DSI clock client pointer. * @byte_clk: Pixel clock rate in Hz. + * @byte_intf_clk: Byte interface clock rate in Hz. * @index: Index of the DSI controller. * return: error code in case of failure or 0 for success. */ -int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index); +int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, + u64 byte_intf_clk, u32 index); /** * dsi_clk_update_parent() - update parent clocks for specified clock diff --git a/msm/dsi/dsi_clk_manager.c b/msm/dsi/dsi_clk_manager.c index 6801a79a1a..c2531bbf23 100644 --- a/msm/dsi/dsi_clk_manager.c +++ b/msm/dsi/dsi_clk_manager.c @@ -130,15 +130,16 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index) * dsi_clk_set_byte_clk_rate() - set frequency for byte clock * @client: DSI clock client pointer. * @byte_clk: Byte clock rate in Hz. + * @byte_intf_clk: Byte interface clock rate in Hz. * @index: Index of the DSI controller. * return: error code in case of failure or 0 for success. */ -int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) +int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, + u64 byte_intf_clk, u32 index) { int rc = 0; struct dsi_clk_client_info *c = client; struct dsi_clk_mngr *mngr; - u64 byte_intf_rate; mngr = c->mngr; rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_clk, byte_clk); @@ -148,12 +149,14 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) mngr->link_clks[index].freq.byte_clk_rate = byte_clk; if (mngr->link_clks[index].hs_clks.byte_intf_clk) { - byte_intf_rate = mngr->link_clks[index].freq.byte_clk_rate / 2; rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_intf_clk, - byte_intf_rate); + byte_intf_clk); if (rc) DSI_ERR("failed to set clk rate for byte intf clk=%d\n", rc); + else + mngr->link_clks[index].freq.byte_intf_clk_rate = + byte_intf_clk; } return rc; @@ -344,12 +347,10 @@ static int dsi_link_hs_clk_set_rate(struct dsi_link_hs_clk_info *link_hs_clks, /* * If byte_intf_clk is present, set rate for that too. - * For DPHY: byte_intf_clk_rate = byte_clk_rate / 2 - * todo: this needs to be revisited when support for CPHY is added */ if (link_hs_clks->byte_intf_clk) { rc = clk_set_rate(link_hs_clks->byte_intf_clk, - (l_clks->freq.byte_clk_rate / 2)); + l_clks->freq.byte_intf_clk_rate); if (rc) { DSI_ERR("set_rate failed for byte_intf_clk rc = %d\n", rc); diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 155feaa28d..248e44922a 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -269,10 +269,6 @@ dsi_ctrl_get_aspace(struct dsi_ctrl *dsi_ctrl, static void dsi_ctrl_flush_cmd_dma_queue(struct dsi_ctrl *dsi_ctrl) { - u32 status; - u32 mask = DSI_CMD_MODE_DMA_DONE; - struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops; - /* * If a command is triggered right after another command, * check if the previous command transfer is completed. If @@ -281,21 +277,8 @@ static void dsi_ctrl_flush_cmd_dma_queue(struct dsi_ctrl *dsi_ctrl) * completed before triggering the next command by * flushing the workqueue. */ - status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); if (atomic_read(&dsi_ctrl->dma_irq_trig)) { cancel_work_sync(&dsi_ctrl->dma_cmd_wait); - } else if (status & mask) { - atomic_set(&dsi_ctrl->dma_irq_trig, 1); - status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); - dsi_hw_ops.clear_interrupt_status( - &dsi_ctrl->hw, - status); - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - complete_all(&dsi_ctrl->irq_info.cmd_dma_done); - cancel_work_sync(&dsi_ctrl->dma_cmd_wait); - DSI_CTRL_DEBUG(dsi_ctrl, - "dma_tx done but irq not yet triggered\n"); } else { flush_workqueue(dsi_ctrl->dma_cmd_workq); } @@ -319,24 +302,11 @@ static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work) */ if (atomic_read(&dsi_ctrl->dma_irq_trig)) goto done; - /* - * If IRQ wasn't triggered check interrupt status register for - * transfer done before waiting. - */ - status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); - if (status & mask) { - status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); - dsi_hw_ops.clear_interrupt_status(&dsi_ctrl->hw, - status); - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - goto done; - } ret = wait_for_completion_timeout( &dsi_ctrl->irq_info.cmd_dma_done, msecs_to_jiffies(DSI_CTRL_TX_TO_MS)); - if (ret == 0) { + if (ret == 0 && !atomic_read(&dsi_ctrl->dma_irq_trig)) { status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); if (status & mask) { status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); @@ -877,9 +847,9 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl, { int rc = 0; u32 num_of_lanes = 0; - u32 bpp, frame_time_us; + u32 bpp, frame_time_us, byte_intf_clk_div; u64 h_period, v_period, bit_rate, pclk_rate, bit_rate_per_lane, - byte_clk_rate; + byte_clk_rate, byte_intf_clk_rate; struct dsi_host_common_cfg *host_cfg = &config->common_config; struct dsi_split_link_config *split_link = &host_cfg->split_link; struct dsi_mode_info *timing = &config->video_timing; @@ -924,14 +894,20 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl, do_div(pclk_rate, bpp); byte_clk_rate = bit_rate_per_lane; do_div(byte_clk_rate, 8); + byte_intf_clk_rate = byte_clk_rate; + byte_intf_clk_div = host_cfg->byte_intf_clk_div; + do_div(byte_intf_clk_rate, byte_intf_clk_div); + DSI_CTRL_DEBUG(dsi_ctrl, "bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", bit_rate, bit_rate_per_lane); - DSI_CTRL_DEBUG(dsi_ctrl, "byte_clk_rate = %llu, pclk_rate = %llu\n", - byte_clk_rate, pclk_rate); + DSI_CTRL_DEBUG(dsi_ctrl, "byte_clk_rate = %llu, byte_intf_clk = %llu\n", + byte_clk_rate, byte_intf_clk_rate); + DSI_CTRL_DEBUG(dsi_ctrl, "pclk_rate = %llu\n", pclk_rate); dsi_ctrl->clk_freq.byte_clk_rate = byte_clk_rate; dsi_ctrl->clk_freq.pix_clk_rate = pclk_rate; dsi_ctrl->clk_freq.esc_clk_rate = config->esc_clk_rate_hz; + dsi_ctrl->clk_freq.byte_intf_clk_rate = byte_intf_clk_rate; config->bit_clk_rate_hz = dsi_ctrl->clk_freq.byte_clk_rate * 8; rc = dsi_clk_set_link_frequencies(clk_handle, dsi_ctrl->clk_freq, @@ -1268,23 +1244,33 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, * result in smmu write faults with DSI as client. */ if (flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) { - dsi_hw_ops.soft_reset(&dsi_ctrl->hw); + if (dsi_ctrl->version < DSI_CTRL_VERSION_2_4) + dsi_hw_ops.soft_reset(&dsi_ctrl->hw); dsi_ctrl->cmd_len = 0; } } } -static u32 dsi_ctrl_validate_msg_flags(const struct mipi_dsi_msg *msg, +static u32 dsi_ctrl_validate_msg_flags(struct dsi_ctrl *dsi_ctrl, + const struct mipi_dsi_msg *msg, u32 flags) { /* - * ASYNC command wait mode is not supported for FIFO commands. - * Waiting after a command is transferred cannot be guaranteed - * if DSI_CTRL_CMD_ASYNC_WAIT flag is set. + * ASYNC command wait mode is not supported for + * - commands sent using DSI FIFO memory + * - DSI read commands + * - DCS commands sent in non-embedded mode + * - whenever an explicit wait time is specificed for the command + * since the wait time cannot be guaranteed in async mode + * - video mode panels */ if ((flags & DSI_CTRL_CMD_FIFO_STORE) || - msg->wait_ms) + flags & DSI_CTRL_CMD_READ || + flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE || + msg->wait_ms || + (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE)) flags &= ~DSI_CTRL_CMD_ASYNC_WAIT; + return flags; } @@ -1313,7 +1299,7 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, goto error; } - flags = dsi_ctrl_validate_msg_flags(msg, flags); + flags = dsi_ctrl_validate_msg_flags(dsi_ctrl, msg, flags); if (dsi_ctrl->dma_wait_queued) dsi_ctrl_flush_cmd_dma_queue(dsi_ctrl); @@ -2692,6 +2678,10 @@ void dsi_ctrl_enable_status_interrupt(struct dsi_ctrl *dsi_ctrl, dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, dsi_ctrl->irq_info.irq_stat_mask); } + + if (intr_idx == DSI_SINT_CMD_MODE_DMA_DONE) + dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, + dsi_ctrl->irq_info.irq_stat_mask); ++(dsi_ctrl->irq_info.irq_stat_refcount[intr_idx]); if (event_info) @@ -3196,7 +3186,8 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) BIT(DSI_FIFO_OVERFLOW), false); if (flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) { - dsi_hw_ops.soft_reset(&dsi_ctrl->hw); + if (dsi_ctrl->version < DSI_CTRL_VERSION_2_4) + dsi_hw_ops.soft_reset(&dsi_ctrl->hw); dsi_ctrl->cmd_len = 0; } } diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 4662b88a6a..86ca54e8a1 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -217,6 +217,22 @@ enum dsi_dfps_type { DSI_DFPS_MAX }; +/** + * enum dsi_dyn_clk_feature_type - Dynamic clock feature support type + * @DSI_DYN_CLK_TYPE_LEGACY: Constant FPS is not supported + * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP: Constant FPS supported with + * change in hfp + * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP: Constant FPS supported with + * change in vfp + * @DSI_DYN_CLK_TYPE_MAX: + */ +enum dsi_dyn_clk_feature_type { + DSI_DYN_CLK_TYPE_LEGACY = 0, + DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP, + DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP, + DSI_DYN_CLK_TYPE_MAX +}; + /** * enum dsi_cmd_set_type - DSI command set type * @DSI_CMD_SET_PRE_ON: Panel pre on @@ -450,6 +466,7 @@ struct dsi_split_link_config { * @ext_bridge_mode: External bridge is connected. * @force_hs_clk_lane: Send continuous clock to the panel. * @dsi_split_link_config: Split Link Configuration. + * @byte_intf_clk_div: Determines the factor for calculating byte intf clock. */ struct dsi_host_common_cfg { enum dsi_pixel_format dst_format; @@ -473,6 +490,7 @@ struct dsi_host_common_cfg { bool ext_bridge_mode; bool force_hs_clk_lane; struct dsi_split_link_config split_link; + u32 byte_intf_clk_div; }; /** diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 9db551d335..a8dcb66efb 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -801,7 +801,7 @@ int dsi_display_check_status(struct drm_connector *connector, void *display, struct dsi_display *dsi_display = display; struct dsi_panel *panel; u32 status_mode; - int rc = 0x1; + int rc = 0x1, ret; u32 mask; if (!dsi_display || !dsi_display->panel) @@ -839,8 +839,10 @@ int dsi_display_check_status(struct drm_connector *connector, void *display, goto exit; } - dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + ret = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON); + if (ret) + goto release_panel_lock; /* Mask error interrupts before attempting ESD read */ mask = BIT(DSI_FIFO_OVERFLOW) | BIT(DSI_FIFO_UNDERFLOW); @@ -3861,6 +3863,22 @@ static bool dsi_display_is_seamless_dfps_possible( return true; } +void dsi_display_update_byte_intf_div(struct dsi_display *display) +{ + struct dsi_host_common_cfg *config; + struct dsi_display_ctrl *m_ctrl; + int phy_ver; + + m_ctrl = &display->ctrl[display->cmd_master_idx]; + config = &display->panel->host_config; + + phy_ver = dsi_phy_get_version(m_ctrl->phy); + if (phy_ver <= DSI_PHY_VERSION_2_0) + config->byte_intf_clk_div = 1; + else + config->byte_intf_clk_div = 2; +} + static int dsi_display_update_dsi_bitrate(struct dsi_display *display, u32 bit_clk_rate) { @@ -3883,8 +3901,9 @@ static int dsi_display_update_dsi_bitrate(struct dsi_display *display, display_for_each_ctrl(i, display) { struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; - u32 num_of_lanes = 0, bpp; - u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; + u32 num_of_lanes = 0, bpp, byte_intf_clk_div; + u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate, + byte_intf_clk_rate; struct dsi_host_common_cfg *host_cfg; mutex_lock(&ctrl->ctrl_lock); @@ -3914,12 +3933,18 @@ static int dsi_display_update_dsi_bitrate(struct dsi_display *display, do_div(pclk_rate, bpp); byte_clk_rate = bit_rate_per_lane; do_div(byte_clk_rate, 8); + byte_intf_clk_rate = byte_clk_rate; + byte_intf_clk_div = host_cfg->byte_intf_clk_div; + do_div(byte_intf_clk_rate, byte_intf_clk_div); + DSI_DEBUG("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", bit_rate, bit_rate_per_lane); - DSI_DEBUG("byte_clk_rate = %llu, pclk_rate = %llu\n", - byte_clk_rate, pclk_rate); + DSI_DEBUG("byte_clk_rate = %llu, byte_intf_clk_rate = %llu\n", + byte_clk_rate, byte_intf_clk_rate); + DSI_DEBUG("pclk_rate = %llu\n", pclk_rate); ctrl->clk_freq.byte_clk_rate = byte_clk_rate; + ctrl->clk_freq.byte_intf_clk_rate = byte_intf_clk_rate; ctrl->clk_freq.pix_clk_rate = pclk_rate; rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, ctrl->clk_freq, ctrl->cell_index); @@ -4013,7 +4038,8 @@ static int _dsi_display_dyn_update_clks(struct dsi_display *display, if (!ctrl->ctrl) continue; rc = dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, - ctrl->ctrl->clk_freq.byte_clk_rate, i); + ctrl->ctrl->clk_freq.byte_clk_rate, + ctrl->ctrl->clk_freq.byte_intf_clk_rate, i); if (rc) { DSI_ERR("failed to set byte rate for index:%d\n", i); goto recover_byte_clk; @@ -4076,7 +4102,8 @@ recover_byte_clk: if (!ctrl->ctrl) continue; dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, - bkp_freq->byte_clk_rate, i); + bkp_freq->byte_clk_rate, + bkp_freq->byte_intf_clk_rate, i); } exit: @@ -4112,6 +4139,7 @@ static int dsi_display_dynamic_clk_switch_vid(struct dsi_display *display, /* back up existing rates to handle failure case */ bkp_freq.byte_clk_rate = m_ctrl->ctrl->clk_freq.byte_clk_rate; + bkp_freq.byte_intf_clk_rate = m_ctrl->ctrl->clk_freq.byte_intf_clk_rate; bkp_freq.pix_clk_rate = m_ctrl->ctrl->clk_freq.pix_clk_rate; bkp_freq.esc_clk_rate = m_ctrl->ctrl->clk_freq.esc_clk_rate; @@ -4196,6 +4224,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, struct dsi_dfps_capabilities dfps_caps; int rc = 0; int i = 0; + struct dsi_dyn_clk_caps *dyn_clk_caps; if (!display || !dsi_mode || !display->panel) { DSI_ERR("Invalid params\n"); @@ -4204,8 +4233,9 @@ static int dsi_display_dfps_update(struct dsi_display *display, timing = &dsi_mode->timing; dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (!dfps_caps.dfps_support) { - DSI_ERR("dfps not supported\n"); + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dfps_caps.dfps_support && !dyn_clk_caps->maintain_const_fps) { + DSI_ERR("dfps or constant fps not supported\n"); return -ENOTSUPP; } @@ -4482,7 +4512,32 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, display->name, rc); goto error; } - } else if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + rc = dsi_ctrl_update_host_config(ctrl->ctrl, + &display->config, mode, mode->dsi_mode_flags, + display->dsi_clk_handle); + if (rc) { + DSI_ERR("failed to update ctrl config\n"); + goto error; + } + } + if (priv_info->phy_timing_len) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + rc = dsi_phy_set_timing_params(ctrl->phy, + priv_info->phy_timing_val, + priv_info->phy_timing_len, + commit_phy_timing); + if (rc) + DSI_ERR("Fail to add timing params\n"); + } + } + if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK)) + return rc; + } + + if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) { if (display->panel->panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_display_dynamic_clk_switch_vid(display, mode); if (rc) @@ -4939,6 +4994,7 @@ static int dsi_display_bind(struct device *dev, } } + dsi_display_update_byte_intf_div(display); rc = dsi_display_mipi_host_init(display); if (rc) { DSI_ERR("[%s] failed to initialize mipi host, rc=%d\n", @@ -5872,6 +5928,53 @@ int dsi_display_get_mode_count(struct dsi_display *display, return 0; } +void dsi_display_adjust_mode_timing( + struct dsi_dyn_clk_caps *dyn_clk_caps, + struct dsi_display_mode *dsi_mode, + int lanes, int bpp) +{ + u64 new_htotal, new_vtotal, htotal, vtotal, old_htotal, div; + + if (!dyn_clk_caps->maintain_const_fps) + return; + /* + * When there is a dynamic clock switch, there is small change + * in FPS. To compensate for this difference in FPS, hfp or vfp + * is adjusted. It has been assumed that the refined porch values + * are supported by the panel. This logic can be enhanced further + * in future by taking min/max porches supported by the panel. + */ + switch (dyn_clk_caps->type) { + case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP: + vtotal = DSI_V_TOTAL(&dsi_mode->timing); + old_htotal = dsi_h_total_dce(&dsi_mode->timing); + new_htotal = dsi_mode->timing.clk_rate_hz * lanes; + div = bpp * vtotal * dsi_mode->timing.refresh_rate; + do_div(new_htotal, div); + if (old_htotal > new_htotal) + dsi_mode->timing.h_front_porch -= + (old_htotal - new_htotal); + else + dsi_mode->timing.h_front_porch += + (new_htotal - old_htotal); + break; + + case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP: + htotal = dsi_h_total_dce(&dsi_mode->timing); + new_vtotal = dsi_mode->timing.clk_rate_hz * lanes; + div = bpp * htotal * dsi_mode->timing.refresh_rate; + do_div(new_vtotal, div); + dsi_mode->timing.v_front_porch = new_vtotal - + dsi_mode->timing.v_back_porch - + dsi_mode->timing.v_sync_width - + dsi_mode->timing.v_active; + break; + + default: + break; + } +} + static void _dsi_display_populate_bit_clks(struct dsi_display *display, int start, int end, u32 *mode_idx) { @@ -5911,6 +6014,9 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, * be based on user or device tree preferrence. */ src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0]; + + dsi_display_adjust_mode_timing(dyn_clk_caps, src, lanes, bpp); + src->pixel_clk_khz = div_u64(src->timing.clk_rate_hz * lanes, bpp); src->pixel_clk_khz /= 1000; @@ -5930,6 +6036,10 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, } memcpy(dst, src, sizeof(struct dsi_display_mode)); dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i]; + + dsi_display_adjust_mode_timing(dyn_clk_caps, dst, lanes, + bpp); + dst->pixel_clk_khz = div_u64(dst->timing.clk_rate_hz * lanes, bpp); dst->pixel_clk_khz /= 1000; @@ -6224,13 +6334,28 @@ int dsi_display_find_mode(struct dsi_display *display, return rc; } +static inline bool dsi_display_mode_switch_dfps(struct dsi_display_mode *cur, + struct dsi_display_mode *adj) +{ + /* + * If there is a change in the hfp or vfp of the current and adjoining + * mode,then either it is a dfps mode switch or dynamic clk change with + * constant fps. + */ + if ((cur->timing.h_front_porch != adj->timing.h_front_porch) || + (cur->timing.v_front_porch != adj->timing.v_front_porch)) + return true; + else + return false; +} + /** * dsi_display_validate_mode_change() - Validate mode change case. * @display: DSI display handle. * @cur_mode: Current mode. * @adj_mode: Mode to be set. * MSM_MODE_FLAG_SEAMLESS_VRR flag is set if there - * is change in fps but vactive and hactive are same. + * is change in hfp or vfp but vactive and hactive are same. * DSI_MODE_FLAG_DYN_CLK flag is set if there * is change in clk but vactive and hactive are same. * Return: error code. @@ -6254,14 +6379,15 @@ int dsi_display_validate_mode_change(struct dsi_display *display, } mutex_lock(&display->display_lock); - + dyn_clk_caps = &(display->panel->dyn_clk_caps); if ((cur_mode->timing.v_active == adj_mode->timing.v_active) && - (cur_mode->timing.h_active == adj_mode->timing.h_active)) { - /* dfps change use case */ - if (cur_mode->timing.refresh_rate != - adj_mode->timing.refresh_rate) { + (cur_mode->timing.h_active == adj_mode->timing.h_active) && + (cur_mode->panel_mode == adj_mode->panel_mode)) { + /* dfps and dynamic clock with const fps use case */ + if (dsi_display_mode_switch_dfps(cur_mode, adj_mode)) { dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (dfps_caps.dfps_support) { + if (dfps_caps.dfps_support || + dyn_clk_caps->maintain_const_fps) { DSI_DEBUG("Mode switch is seamless variable refresh\n"); adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; SDE_EVT32(cur_mode->timing.refresh_rate, @@ -6273,10 +6399,11 @@ int dsi_display_validate_mode_change(struct dsi_display *display, /* dynamic clk change use case */ if (cur_mode->pixel_clk_khz != adj_mode->pixel_clk_khz) { - dyn_clk_caps = &(display->panel->dyn_clk_caps); if (dyn_clk_caps->dyn_clk_support) { DSI_DEBUG("dynamic clk change detected\n"); - if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) { + if ((adj_mode->dsi_mode_flags & + DSI_MODE_FLAG_VRR) && + (!dyn_clk_caps->maintain_const_fps)) { DSI_ERR("dfps and dyn clk not supported in same commit\n"); rc = -ENOTSUPP; goto error; @@ -6794,12 +6921,14 @@ int dsi_display_prepare(struct dsi_display *display) goto error; } - /* update dsi ctrl for new mode */ - rc = dsi_display_pre_switch(display); - if (rc) - DSI_ERR("[%s] panel pre-prepare-res-switch failed, rc=%d\n", + if (!display->is_cont_splash_enabled) { + /* update dsi ctrl for new mode */ + rc = dsi_display_pre_switch(display); + if (rc) + DSI_ERR("[%s] panel pre-switch failed, rc=%d\n", display->name, rc); - goto error; + goto error; + } } if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) && diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index fbea1f3e2e..9494d0d931 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -253,6 +253,7 @@ static void dsi_bridge_enable(struct drm_bridge *bridge) static void dsi_bridge_disable(struct drm_bridge *bridge) { int rc = 0; + int private_flags; struct dsi_display *display; struct dsi_bridge *c_bridge = to_dsi_bridge(bridge); @@ -261,18 +262,14 @@ static void dsi_bridge_disable(struct drm_bridge *bridge) return; } display = c_bridge->display; + private_flags = + bridge->encoder->crtc->state->adjusted_mode.private_flags; if (display && display->drm_conn) { - if (bridge->encoder->crtc->state->adjusted_mode.private_flags & - MSM_MODE_FLAG_SEAMLESS_POMS) { - display->poms_pending = true; - /* Disable ESD thread, during panel mode switch */ - sde_connector_schedule_status_work(display->drm_conn, - false); - } else { - display->poms_pending = false; - sde_connector_helper_bridge_disable(display->drm_conn); - } + display->poms_pending = + private_flags & MSM_MODE_FLAG_SEAMLESS_POMS; + + sde_connector_helper_bridge_disable(display->drm_conn); } rc = dsi_display_pre_disable(c_bridge->display); @@ -426,6 +423,7 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, /* Reject seamless transition when active changed */ if (crtc_state->active_changed && ((dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR) || + (dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_POMS) || (dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK))) { DSI_ERR("seamless upon active changed 0x%x %d\n", dsi_mode.dsi_mode_flags, crtc_state->active_changed); diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 02a7f87b89..6ef7b9d038 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1175,6 +1175,7 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps; struct dsi_parser_utils *utils = &panel->utils; const char *name = panel->name; + const char *type; supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable"); @@ -1207,6 +1208,24 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) dyn_clk_caps->dyn_clk_support = true; + type = utils->get_property(utils->data, + "qcom,dsi-dyn-clk-type", NULL); + if (!type) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY; + dyn_clk_caps->maintain_const_fps = false; + return 0; + } + if (!strcmp(type, "constant-fps-adjust-hfp")) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP; + dyn_clk_caps->maintain_const_fps = true; + } else if (!strcmp(type, "constant-fps-adjust-vfp")) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP; + dyn_clk_caps->maintain_const_fps = true; + } else { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY; + dyn_clk_caps->maintain_const_fps = false; + } + DSI_DEBUG("Dynamic clock type is [%s]\n", type); return 0; } @@ -3704,9 +3723,11 @@ int dsi_panel_get_mode(struct dsi_panel *panel, if (panel->panel_mode_switch_enabled) { rc = dsi_panel_parse_panel_mode_caps(mode, utils); if (rc) { - DSI_ERR("PMS: failed to parse panel mode\n"); rc = 0; mode->panel_mode = panel->panel_mode; + DSI_INFO( + "POMS: panel mode isn't specified in timing[%d]\n", + child_idx); } } else { mode->panel_mode = panel->panel_mode; diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index 07d00450e4..9a77497022 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -82,6 +82,8 @@ struct dsi_dyn_clk_caps { bool dyn_clk_support; u32 *bit_clk_list; u32 bit_clk_list_len; + enum dsi_dyn_clk_feature_type type; + bool maintain_const_fps; }; struct dsi_pinctrl_info { diff --git a/msm/dsi/dsi_parser.c b/msm/dsi/dsi_parser.c index fb587c3c36..cef5fe70cf 100644 --- a/msm/dsi/dsi_parser.c +++ b/msm/dsi/dsi_parser.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include @@ -239,15 +239,18 @@ static bool dsi_parser_parse_prop(struct device *dev, { bool found = false; char *out = strsep(&buf, "="); + size_t buf_len; if (!out || !buf) goto end; - prop->raw = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + buf_len = strlen(buf); + + prop->raw = devm_kzalloc(dev, buf_len + 1, GFP_KERNEL); if (!prop->raw) goto end; - strlcpy(prop->raw, buf, strlen(buf) + 1); + strlcpy(prop->raw, buf, buf_len + 1); found = true; diff --git a/msm/dsi/dsi_phy.c b/msm/dsi/dsi_phy.c index 830016e94f..86852737d8 100644 --- a/msm/dsi/dsi_phy.c +++ b/msm/dsi/dsi_phy.c @@ -111,6 +111,11 @@ static const struct of_device_id msm_dsi_phy_of_match[] = { {} }; +int dsi_phy_get_version(struct msm_dsi_phy *phy) +{ + return phy->ver_info->version; +} + static int dsi_phy_regmap_init(struct platform_device *pdev, struct msm_dsi_phy *phy) { diff --git a/msm/dsi/dsi_phy.h b/msm/dsi/dsi_phy.h index 1068304612..dc9d44be2f 100644 --- a/msm/dsi/dsi_phy.h +++ b/msm/dsi/dsi_phy.h @@ -125,6 +125,14 @@ struct msm_dsi_phy *dsi_phy_get(struct device_node *of_node); */ void dsi_phy_put(struct msm_dsi_phy *dsi_phy); +/** + * dsi_phy_get_version() - returns dsi phy version + * @dsi_phy: DSI PHY handle. + * + * Return: phy version + */ +int dsi_phy_get_version(struct msm_dsi_phy *phy); + /** * dsi_phy_drv_init() - initialize dsi phy driver * @dsi_phy: DSI PHY handle. diff --git a/msm/dsi/dsi_phy_hw_v2_0.c b/msm/dsi/dsi_phy_hw_v2_0.c index 5206469234..835e2cf3f0 100644 --- a/msm/dsi/dsi_phy_hw_v2_0.c +++ b/msm/dsi/dsi_phy_hw_v2_0.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include @@ -64,6 +64,83 @@ #define DSIPHY_PLL_CLKBUFLR_EN 0x041C #define DSIPHY_PLL_PLL_BANDGAP 0x0508 +/* dynamic refresh control registers */ +#define DSI_DYN_REFRESH_CTRL 0x000 +#define DSI_DYN_REFRESH_PIPE_DELAY 0x004 +#define DSI_DYN_REFRESH_PIPE_DELAY2 0x008 +#define DSI_DYN_REFRESH_PLL_DELAY 0x00C +#define DSI_DYN_REFRESH_STATUS 0x010 +#define DSI_DYN_REFRESH_PLL_CTRL0 0x014 +#define DSI_DYN_REFRESH_PLL_CTRL1 0x018 +#define DSI_DYN_REFRESH_PLL_CTRL2 0x01C +#define DSI_DYN_REFRESH_PLL_CTRL3 0x020 +#define DSI_DYN_REFRESH_PLL_CTRL4 0x024 +#define DSI_DYN_REFRESH_PLL_CTRL5 0x028 +#define DSI_DYN_REFRESH_PLL_CTRL6 0x02C +#define DSI_DYN_REFRESH_PLL_CTRL7 0x030 +#define DSI_DYN_REFRESH_PLL_CTRL8 0x034 +#define DSI_DYN_REFRESH_PLL_CTRL9 0x038 +#define DSI_DYN_REFRESH_PLL_CTRL10 0x03C +#define DSI_DYN_REFRESH_PLL_CTRL11 0x040 +#define DSI_DYN_REFRESH_PLL_CTRL12 0x044 +#define DSI_DYN_REFRESH_PLL_CTRL13 0x048 +#define DSI_DYN_REFRESH_PLL_CTRL14 0x04C +#define DSI_DYN_REFRESH_PLL_CTRL15 0x050 +#define DSI_DYN_REFRESH_PLL_CTRL16 0x054 +#define DSI_DYN_REFRESH_PLL_CTRL17 0x058 +#define DSI_DYN_REFRESH_PLL_CTRL18 0x05C +#define DSI_DYN_REFRESH_PLL_CTRL19 0x060 +#define DSI_DYN_REFRESH_PLL_CTRL20 0x064 +#define DSI_DYN_REFRESH_PLL_CTRL21 0x068 +#define DSI_DYN_REFRESH_PLL_CTRL22 0x06C +#define DSI_DYN_REFRESH_PLL_CTRL23 0x070 +#define DSI_DYN_REFRESH_PLL_CTRL24 0x074 +#define DSI_DYN_REFRESH_PLL_CTRL25 0x078 +#define DSI_DYN_REFRESH_PLL_CTRL26 0x07C +#define DSI_DYN_REFRESH_PLL_CTRL27 0x080 +#define DSI_DYN_REFRESH_PLL_CTRL28 0x084 +#define DSI_DYN_REFRESH_PLL_CTRL29 0x088 +#define DSI_DYN_REFRESH_PLL_CTRL30 0x08C +#define DSI_DYN_REFRESH_PLL_CTRL31 0x090 +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR 0x094 +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR2 0x098 + +#define DSIPHY_DLN0_CFG1 0x0104 +#define DSIPHY_DLN0_TIMING_CTRL_4 0x0118 +#define DSIPHY_DLN0_TIMING_CTRL_5 0x011C +#define DSIPHY_DLN0_TIMING_CTRL_6 0x0120 +#define DSIPHY_DLN0_TIMING_CTRL_7 0x0124 +#define DSIPHY_DLN0_TIMING_CTRL_8 0x0128 + +#define DSIPHY_DLN1_CFG1 0x0184 +#define DSIPHY_DLN1_TIMING_CTRL_4 0x0198 +#define DSIPHY_DLN1_TIMING_CTRL_5 0x019C +#define DSIPHY_DLN1_TIMING_CTRL_6 0x01A0 +#define DSIPHY_DLN1_TIMING_CTRL_7 0x01A4 +#define DSIPHY_DLN1_TIMING_CTRL_8 0x01A8 + +#define DSIPHY_DLN2_CFG1 0x0204 +#define DSIPHY_DLN2_TIMING_CTRL_4 0x0218 +#define DSIPHY_DLN2_TIMING_CTRL_5 0x021C +#define DSIPHY_DLN2_TIMING_CTRL_6 0x0220 +#define DSIPHY_DLN2_TIMING_CTRL_7 0x0224 +#define DSIPHY_DLN2_TIMING_CTRL_8 0x0228 + +#define DSIPHY_DLN3_CFG1 0x0284 +#define DSIPHY_DLN3_TIMING_CTRL_4 0x0298 +#define DSIPHY_DLN3_TIMING_CTRL_5 0x029C +#define DSIPHY_DLN3_TIMING_CTRL_6 0x02A0 +#define DSIPHY_DLN3_TIMING_CTRL_7 0x02A4 +#define DSIPHY_DLN3_TIMING_CTRL_8 0x02A8 + +#define DSIPHY_CKLN_CFG1 0x0304 +#define DSIPHY_CKLN_TIMING_CTRL_4 0x0318 +#define DSIPHY_CKLN_TIMING_CTRL_5 0x031C +#define DSIPHY_CKLN_TIMING_CTRL_6 0x0320 +#define DSIPHY_CKLN_TIMING_CTRL_7 0x0324 +#define DSIPHY_CKLN_TIMING_CTRL_8 0x0328 + +#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c /** * regulator_enable() - enable regulators for DSI PHY * @phy: Pointer to DSI PHY hardware object. @@ -299,3 +376,261 @@ void dsi_phy_hw_v2_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable) DSI_PHY_DBG(phy, "clamp disabled\n"); } } + +void dsi_phy_hw_v2_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master) +{ + u32 glbl_tst_cntrl; + + if (is_master) { + glbl_tst_cntrl = DSI_R32(phy, DSIPHY_CMN_GLBL_TEST_CTRL); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL0, + DSIPHY_CMN_GLBL_TEST_CTRL, + DSIPHY_PLL_PLL_BANDGAP, + glbl_tst_cntrl | BIT(1), 0x1); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL1, + DSIPHY_PLL_RESETSM_CNTRL5, + DSIPHY_PLL_PLL_BANDGAP, 0x0D, 0x03); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL2, + DSIPHY_PLL_RESETSM_CNTRL5, + DSIPHY_CMN_PLL_CNTRL, 0x1D, 0x00); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL3, + DSIPHY_CMN_CTRL_1, DSIPHY_DLN0_CFG1, 0x20, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL4, + DSIPHY_DLN1_CFG1, DSIPHY_DLN2_CFG1, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL5, + DSIPHY_DLN3_CFG1, DSIPHY_CKLN_CFG1, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL6, + DSIPHY_DLN0_TIMING_CTRL_4, + DSIPHY_DLN1_TIMING_CTRL_4, + cfg->timing.lane[0][0], cfg->timing.lane[1][0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL7, + DSIPHY_DLN2_TIMING_CTRL_4, + DSIPHY_DLN3_TIMING_CTRL_4, + cfg->timing.lane[2][0], cfg->timing.lane[3][0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL8, + DSIPHY_CKLN_TIMING_CTRL_4, + DSIPHY_DLN0_TIMING_CTRL_5, + cfg->timing.lane[4][0], cfg->timing.lane[0][1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_DLN1_TIMING_CTRL_5, + DSIPHY_DLN2_TIMING_CTRL_5, + cfg->timing.lane[1][1], cfg->timing.lane[2][1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL10, + DSIPHY_DLN3_TIMING_CTRL_5, + DSIPHY_CKLN_TIMING_CTRL_5, + cfg->timing.lane[3][1], cfg->timing.lane[4][1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL11, + DSIPHY_DLN0_TIMING_CTRL_6, + DSIPHY_DLN1_TIMING_CTRL_6, + cfg->timing.lane[0][2], cfg->timing.lane[1][2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL12, + DSIPHY_DLN2_TIMING_CTRL_6, + DSIPHY_DLN3_TIMING_CTRL_6, + cfg->timing.lane[2][2], cfg->timing.lane[3][2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL13, + DSIPHY_CKLN_TIMING_CTRL_6, + DSIPHY_DLN0_TIMING_CTRL_7, + cfg->timing.lane[4][2], cfg->timing.lane[0][3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL14, + DSIPHY_DLN1_TIMING_CTRL_7, + DSIPHY_DLN2_TIMING_CTRL_7, + cfg->timing.lane[1][3], cfg->timing.lane[2][3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL15, + DSIPHY_DLN3_TIMING_CTRL_7, + DSIPHY_CKLN_TIMING_CTRL_7, + cfg->timing.lane[3][3], cfg->timing.lane[4][3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, + DSI_DYN_REFRESH_PLL_CTRL16, + DSIPHY_DLN0_TIMING_CTRL_8, + DSIPHY_DLN1_TIMING_CTRL_8, + cfg->timing.lane[0][4], cfg->timing.lane[1][4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL17, + DSIPHY_DLN2_TIMING_CTRL_8, + DSIPHY_DLN3_TIMING_CTRL_8, + cfg->timing.lane[2][4], cfg->timing.lane[3][4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL18, + DSIPHY_CKLN_TIMING_CTRL_8, DSIPHY_CMN_CTRL_1, + cfg->timing.lane[4][4], 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL30, + DSIPHY_CMN_GLBL_TEST_CTRL, + DSIPHY_CMN_GLBL_TEST_CTRL, + ((glbl_tst_cntrl) & (~BIT(2))), + ((glbl_tst_cntrl) & (~BIT(2)))); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL31, + DSIPHY_CMN_GLBL_TEST_CTRL, + DSIPHY_CMN_GLBL_TEST_CTRL, + ((glbl_tst_cntrl) & (~BIT(2))), + ((glbl_tst_cntrl) & (~BIT(2)))); + } else { + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL0, + DSIPHY_DLN0_CFG1, DSIPHY_DLN1_CFG1, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL1, + DSIPHY_DLN2_CFG1, DSIPHY_DLN3_CFG1, 0x0, 0x0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL2, + DSIPHY_CKLN_CFG1, DSIPHY_DLN0_TIMING_CTRL_4, + 0x0, cfg->timing.lane[0][0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL3, + DSIPHY_DLN1_TIMING_CTRL_4, + DSIPHY_DLN2_TIMING_CTRL_4, + cfg->timing.lane[1][0], cfg->timing.lane[2][0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL4, + DSIPHY_DLN3_TIMING_CTRL_4, + DSIPHY_CKLN_TIMING_CTRL_4, + cfg->timing.lane[3][0], cfg->timing.lane[4][0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL5, + DSIPHY_DLN0_TIMING_CTRL_5, + DSIPHY_DLN1_TIMING_CTRL_5, + cfg->timing.lane[0][1], cfg->timing.lane[1][1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL6, + DSIPHY_DLN2_TIMING_CTRL_5, + DSIPHY_DLN3_TIMING_CTRL_5, + cfg->timing.lane[2][1], cfg->timing.lane[3][1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL7, + DSIPHY_CKLN_TIMING_CTRL_5, + DSIPHY_DLN0_TIMING_CTRL_6, + cfg->timing.lane[4][1], cfg->timing.lane[0][2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL8, + DSIPHY_DLN1_TIMING_CTRL_6, + DSIPHY_DLN2_TIMING_CTRL_6, + cfg->timing.lane[1][2], cfg->timing.lane[2][2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_DLN3_TIMING_CTRL_6, + DSIPHY_CKLN_TIMING_CTRL_6, + cfg->timing.lane[3][2], cfg->timing.lane[4][2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL10, + DSIPHY_DLN0_TIMING_CTRL_7, + DSIPHY_DLN1_TIMING_CTRL_7, + cfg->timing.lane[0][3], cfg->timing.lane[1][3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL11, + DSIPHY_DLN2_TIMING_CTRL_7, + DSIPHY_DLN3_TIMING_CTRL_7, + cfg->timing.lane[2][3], cfg->timing.lane[3][3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL12, + DSIPHY_CKLN_TIMING_CTRL_7, + DSIPHY_DLN0_TIMING_CTRL_8, + cfg->timing.lane[4][3], cfg->timing.lane[0][4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL13, + DSIPHY_DLN1_TIMING_CTRL_8, + DSIPHY_DLN2_TIMING_CTRL_8, + cfg->timing.lane[1][4], cfg->timing.lane[2][4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL14, + DSIPHY_DLN3_TIMING_CTRL_8, + DSIPHY_CKLN_TIMING_CTRL_8, + cfg->timing.lane[3][4], cfg->timing.lane[4][4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL15, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL16, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL17, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL18, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL19, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL20, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL21, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL22, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL23, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL24, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL25, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL26, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL27, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL28, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL29, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL30, + 0x0110, 0x0110, 0, 0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL31, + 0x0110, 0x0110, 0, 0); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_UPPER_ADDR, + 0x0); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_UPPER_ADDR2, + 0x0); + } + + wmb(); /* make sure phy timings are updated*/ +} + +void dsi_phy_hw_v2_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay) +{ + if (!delay) + return; + + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY, + delay->pipe_delay); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY2, + delay->pipe_delay2); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_DELAY, + delay->pll_delay); +} + +void dsi_phy_hw_v2_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset) +{ + u32 reg; + + /* + * if no offset is mentioned then this means we want to clear + * the dynamic refresh ctrl register which is the last step + * of dynamic refresh sequence. + */ + if (!offset) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg &= ~(BIT(0) | BIT(8)); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is cleared */ + return; + } + + if (offset & BIT(DYN_REFRESH_INTF_SEL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(13); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SWI_CTRL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(0); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SW_TRIGGER)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(8); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is triggered */ + } +} + +int dsi_phy_hw_v2_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size) +{ + int i, j, count = 0; + + if (!timings || !dst || !size) + return -EINVAL; + + if (size != (DSI_LANE_MAX * DSI_MAX_SETTINGS)) { + pr_err("size mis-match\n"); + return -EINVAL; + } + + for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) { + for (j = 0; j < DSI_MAX_SETTINGS; j++) { + dst[count] = timings->lane[i][j]; + count++; + } + } + + return 0; +} diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index bd7c1e5ac0..e9617e42de 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -94,8 +94,7 @@ static int sde_backlight_device_update_status(struct backlight_device *bd) if (!bl_lvl && brightness) bl_lvl = 1; - if (display->panel->bl_config.bl_update == - BL_UPDATE_DELAY_UNTIL_FIRST_FRAME && !c_conn->allow_bl_update) { + if (!c_conn->allow_bl_update) { c_conn->unset_bl_level = bl_lvl; return 0; } @@ -532,9 +531,7 @@ static int _sde_connector_update_bl_scale(struct sde_connector *c_conn) bl_config = &dsi_display->panel->bl_config; - if (dsi_display->panel->bl_config.bl_update == - BL_UPDATE_DELAY_UNTIL_FIRST_FRAME && - !c_conn->allow_bl_update) { + if (!c_conn->allow_bl_update) { c_conn->unset_bl_level = bl_config->bl_level; return 0; } @@ -797,21 +794,29 @@ void sde_connector_helper_bridge_disable(struct drm_connector *connector) { int rc; struct sde_connector *c_conn = NULL; + struct dsi_display *display; + bool poms_pending = false; if (!connector) return; - rc = _sde_connector_update_dirty_properties(connector); - if (rc) { - SDE_ERROR("conn %d final pre kickoff failed %d\n", - connector->base.id, rc); - SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR); + c_conn = to_sde_connector(connector); + if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) { + display = (struct dsi_display *) c_conn->display; + poms_pending = display->poms_pending; } + if (!poms_pending) { + rc = _sde_connector_update_dirty_properties(connector); + if (rc) { + SDE_ERROR("conn %d final pre kickoff failed %d\n", + connector->base.id, rc); + SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR); + } + } /* Disable ESD thread */ sde_connector_schedule_status_work(connector, false); - c_conn = to_sde_connector(connector); if (c_conn->bl_device) { c_conn->bl_device->props.power = FB_BLANK_POWERDOWN; c_conn->bl_device->props.state |= BL_CORE_FBBLANK; @@ -2167,6 +2172,7 @@ static void sde_connector_check_status_work(struct work_struct *work) { struct sde_connector *conn; int rc = 0; + struct device *dev; conn = container_of(to_delayed_work(work), struct sde_connector, status_work); @@ -2176,7 +2182,9 @@ static void sde_connector_check_status_work(struct work_struct *work) } mutex_lock(&conn->lock); - if (!conn->ops.check_status || + dev = conn->base.dev->dev; + + if (!conn->ops.check_status || dev->power.is_suspended || (conn->dpms_mode != DRM_MODE_DPMS_ON)) { SDE_DEBUG("dpms mode: %d\n", conn->dpms_mode); mutex_unlock(&conn->lock);