diff options
author | Dave Airlie <airlied@redhat.com> | 2023-06-19 16:01:45 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2023-06-19 16:01:46 +1000 |
commit | 2222dcb0775d36de28992f56455ab3967b30d380 (patch) | |
tree | 231b4c4fb1b51a1371ca6200947241d643c94148 /drivers/gpu/drm/msm/dsi | |
parent | cce3b573a52a41dd7face9dbf745f10f9bf4632b (diff) | |
parent | cd036d542afb82adfbbd43c5dbeb7010e8e91ee7 (diff) | |
download | lwn-2222dcb0775d36de28992f56455ab3967b30d380.tar.gz lwn-2222dcb0775d36de28992f56455ab3967b30d380.zip |
Merge tag 'drm-msm-next-2023-06-18' of https://gitlab.freedesktop.org/drm/msm into drm-next
Updates for v6.5.. this includes a backmerg of drm-next tree to be able
to use new DRM DSC helpers.
Core:
+ Add Marijn Suijten as drm/msm reviewer
+ Adreno A660 bindings
+ SM8350 MDSS bindings fix
+ Fix adreno_is_a690() warnings
+ More generic (DRM) and MSM-specific DSC helpers
DP:
+ Removed obsolete USB-PD remains
+ Documented DP compatible string for sm8550 platform
DPU:
+ Enable missing features (DSPP, DSC, split display) on sc8180x,
sc8280xp, sm8450
+ Enabled writeback on sc7280
+ Implemented tearcheck support to support vsync on SM150 and
newer platforms
+ Native HDMI output support
+ Dropped unused features: regdma, GC, IGC
+ Fixed the DSC flush operations
+ Simplified QoS handling, removing obsolete and unused features
and merging SSPP and WB code paths
+ Reworked dpu_encoder initialisation path
+ Enabled DSPP support on sdm845
+ Disabled color-management if DSPP blocks are not available
+ Added support for DSC 1.2 blocks found on sm8350 and later
+ Added .fb_dirty to fix CMD panels
DSI:
+ Drop powerup quirks in favour of using pre_enable_prev_first for
downstream bridges
+ Fixed 14nm DSI PHY programming
+ Added support for DSI and 28nm DSI PHY on MSM8226 platform
+ Make use of DRM and MSM DSC helpers
MDP5:
+ Added support for display controller on MSM8226 platform
GPU:
+ A690 support
+ Don't set IO_PGTABLE_QUIRK_ARM_OUTER_WBWA on devices with coherent SMMU
(like A690)
+ Move cmdstream dumping out of fence signaling path
+ Cleanups
+ Support for a6xx devices without GMU (aka "GMU wrapper"
+ a610 support
+ a619_holi support (a619 variant without GMU)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rob Clark <robdclark@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGsUB=tRB4nR6ZCJMuLhro5zN3BQWUSywVYbaipqqDZ_cQ@mail.gmail.com
Diffstat (limited to 'drivers/gpu/drm/msm/dsi')
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_cfg.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_cfg.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_host.c | 177 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_manager.c | 68 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/phy/dsi_phy.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c | 97 |
8 files changed, 192 insertions, 161 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index 29ccd755cc2e..8a5fb6df7210 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -245,6 +245,8 @@ static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { &apq8064_dsi_cfg, &msm_dsi_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0, &msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0_2, + &msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1, &msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1_1, diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index 91bdaf50bb1a..43f0dd74edb6 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -11,6 +11,7 @@ #define MSM_DSI_VER_MAJOR_V2 0x02 #define MSM_DSI_VER_MAJOR_6G 0x03 #define MSM_DSI_6G_VER_MINOR_V1_0 0x10000000 +#define MSM_DSI_6G_VER_MINOR_V1_0_2 0x10000002 #define MSM_DSI_6G_VER_MINOR_V1_1 0x10010000 #define MSM_DSI_6G_VER_MINOR_V1_1_1 0x10010001 #define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 961689a255c4..3f6dfb4f9d5a 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -28,6 +28,7 @@ #include "dsi.xml.h" #include "sfpb.xml.h" #include "dsi_cfg.h" +#include "msm_dsc_helper.h" #include "msm_kms.h" #include "msm_gem.h" #include "phy/dsi_phy.h" @@ -117,8 +118,6 @@ struct msm_dsi_host { struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; - struct clk *byte_clk_src; - struct clk *pixel_clk_src; struct clk *byte_intf_clk; unsigned long byte_clk_rate; @@ -128,8 +127,6 @@ struct msm_dsi_host { /* DSI v2 specific clocks */ struct clk *src_clk; - struct clk *esc_clk_src; - struct clk *dsi_clk_src; unsigned long src_clk_rate; @@ -266,21 +263,6 @@ int dsi_clk_init_v2(struct msm_dsi_host *msm_host) return ret; } - msm_host->esc_clk_src = clk_get_parent(msm_host->esc_clk); - if (!msm_host->esc_clk_src) { - ret = -ENODEV; - pr_err("%s: can't get esc clock parent. ret=%d\n", - __func__, ret); - return ret; - } - - msm_host->dsi_clk_src = clk_get_parent(msm_host->src_clk); - if (!msm_host->dsi_clk_src) { - ret = -ENODEV; - pr_err("%s: can't get src clock parent. ret=%d\n", - __func__, ret); - } - return ret; } @@ -345,20 +327,6 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) goto exit; } - msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk); - if (IS_ERR(msm_host->byte_clk_src)) { - ret = PTR_ERR(msm_host->byte_clk_src); - pr_err("%s: can't find byte_clk clock. ret=%d\n", __func__, ret); - goto exit; - } - - msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk); - if (IS_ERR(msm_host->pixel_clk_src)) { - ret = PTR_ERR(msm_host->pixel_clk_src); - pr_err("%s: can't find pixel_clk clock. ret=%d\n", __func__, ret); - goto exit; - } - if (cfg_hnd->ops->clk_init_ver) ret = cfg_hnd->ops->clk_init_ver(msm_host); exit: @@ -560,12 +528,27 @@ void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) clk_disable_unprepare(msm_host->byte_clk); } -static unsigned long dsi_get_pclk_rate(const struct drm_display_mode *mode, bool is_bonded_dsi) +static unsigned long dsi_adjust_pclk_for_compression(const struct drm_display_mode *mode, + const struct drm_dsc_config *dsc) +{ + int new_hdisplay = DIV_ROUND_UP(mode->hdisplay * drm_dsc_get_bpp_int(dsc), + dsc->bits_per_component * 3); + + int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay; + + return new_htotal * mode->vtotal * drm_mode_vrefresh(mode); +} + +static unsigned long dsi_get_pclk_rate(const struct drm_display_mode *mode, + const struct drm_dsc_config *dsc, bool is_bonded_dsi) { unsigned long pclk_rate; pclk_rate = mode->clock * 1000; + if (dsc) + pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc); + /* * For bonded DSI mode, the current DRM mode has the complete width of the * panel. Since, the complete panel is driven by two DSI controllers, @@ -584,8 +567,8 @@ unsigned long dsi_byte_clk_get_rate(struct mipi_dsi_host *host, bool is_bonded_d struct msm_dsi_host *msm_host = to_msm_dsi_host(host); u8 lanes = msm_host->lanes; u32 bpp = dsi_get_bpp(msm_host->format); - unsigned long pclk_rate = dsi_get_pclk_rate(mode, is_bonded_dsi); - u64 pclk_bpp = (u64)pclk_rate * bpp; + unsigned long pclk_rate = dsi_get_pclk_rate(mode, msm_host->dsc, is_bonded_dsi); + unsigned long pclk_bpp; if (lanes == 0) { pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); @@ -594,16 +577,16 @@ unsigned long dsi_byte_clk_get_rate(struct mipi_dsi_host *host, bool is_bonded_d /* CPHY "byte_clk" is in units of 16 bits */ if (msm_host->cphy_mode) - do_div(pclk_bpp, (16 * lanes)); + pclk_bpp = mult_frac(pclk_rate, bpp, 16 * lanes); else - do_div(pclk_bpp, (8 * lanes)); + pclk_bpp = mult_frac(pclk_rate, bpp, 8 * lanes); return pclk_bpp; } static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { - msm_host->pixel_clk_rate = dsi_get_pclk_rate(msm_host->mode, is_bonded_dsi); + msm_host->pixel_clk_rate = dsi_get_pclk_rate(msm_host->mode, msm_host->dsc, is_bonded_dsi); msm_host->byte_clk_rate = dsi_byte_clk_get_rate(&msm_host->base, is_bonded_dsi, msm_host->mode); @@ -627,15 +610,12 @@ int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { u32 bpp = dsi_get_bpp(msm_host->format); - u64 pclk_bpp; unsigned int esc_mhz, esc_div; unsigned long byte_mhz; dsi_calc_pclk(msm_host, is_bonded_dsi); - pclk_bpp = (u64)dsi_get_pclk_rate(msm_host->mode, is_bonded_dsi) * bpp; - do_div(pclk_bpp, 8); - msm_host->src_clk_rate = pclk_bpp; + msm_host->src_clk_rate = mult_frac(msm_host->pixel_clk_rate, bpp, 8); /* * esc clock is byte clock followed by a 4 bit divider, @@ -725,7 +705,12 @@ static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( } } -static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, +static void dsi_ctrl_disable(struct msm_dsi_host *msm_host) +{ + dsi_write(msm_host, REG_DSI_CTRL, 0); +} + +static void dsi_ctrl_enable(struct msm_dsi_host *msm_host, struct msm_dsi_phy_shared_timings *phy_shared_timings, struct msm_dsi_phy *phy) { u32 flags = msm_host->mode_flags; @@ -733,11 +718,6 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; u32 data = 0, lane_ctrl = 0; - if (!enable) { - dsi_write(msm_host, REG_DSI_CTRL, 0); - return; - } - if (flags & MIPI_DSI_MODE_VIDEO) { if (flags & MIPI_DSI_MODE_VIDEO_HSE) data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; @@ -822,7 +802,7 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) { lane_ctrl = dsi_read(msm_host, REG_DSI_LANE_CTRL); - if (msm_dsi_phy_set_continuous_clock(phy, enable)) + if (msm_dsi_phy_set_continuous_clock(phy, true)) lane_ctrl &= ~DSI_LANE_CTRL_HS_REQ_SEL_PHY; dsi_write(msm_host, REG_DSI_LANE_CTRL, @@ -848,20 +828,19 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod /* first calculate dsc parameters and then program * compress mode registers */ - slice_per_intf = DIV_ROUND_UP(hdisplay, dsc->slice_width); - - /* - * If slice_count is greater than slice_per_intf - * then default to 1. This can happen during partial - * update. - */ - if (dsc->slice_count > slice_per_intf) - dsc->slice_count = 1; + slice_per_intf = msm_dsc_get_slices_per_intf(dsc, hdisplay); total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; eol_byte_num = total_bytes_per_intf % 3; - pkt_per_line = slice_per_intf / dsc->slice_count; + + /* + * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. + * + * Since the current driver only supports slice_per_pkt = 1, + * pkt_per_line will be equal to slice per intf for now. + */ + pkt_per_line = slice_per_intf; if (is_cmd_mode) /* packet data type */ reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); @@ -951,7 +930,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) * pulse width same */ h_total -= hdisplay; - hdisplay /= 3; + hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc), 3); h_total += hdisplay; ha_end = ha_start + hdisplay; } @@ -985,7 +964,14 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) if (!msm_host->dsc) wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; else - wc = msm_host->dsc->slice_chunk_size * msm_host->dsc->slice_count + 1; + /* + * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. + * Currently, the driver only supports default value of slice_per_pkt = 1 + * + * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info + * and adjust DSC math to account for slice_per_pkt. + */ + wc = msm_host->dsc->slice_chunk_size + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | @@ -1731,28 +1717,9 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, return -EINVAL; } -static u32 dsi_dsc_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = { - 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, - 0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e -}; - -/* only 8bpc, 8bpp added */ -static char min_qp[DSC_NUM_BUF_RANGES] = { - 0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13 -}; - -static char max_qp[DSC_NUM_BUF_RANGES] = { - 4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15 -}; - -static char bpg_offset[DSC_NUM_BUF_RANGES] = { - 2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12 -}; - static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc) { - int i; - u16 bpp = dsc->bits_per_pixel >> 4; + int ret; if (dsc->bits_per_pixel & 0xf) { DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n"); @@ -1764,49 +1731,23 @@ static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc return -EOPNOTSUPP; } - dsc->rc_model_size = 8192; - dsc->first_line_bpg_offset = 12; - dsc->rc_edge_factor = 6; - dsc->rc_tgt_offset_high = 3; - dsc->rc_tgt_offset_low = 3; dsc->simple_422 = 0; dsc->convert_rgb = 1; dsc->vbr_enable = 0; - /* handle only bpp = bpc = 8 */ - for (i = 0; i < DSC_NUM_BUF_RANGES - 1 ; i++) - dsc->rc_buf_thresh[i] = dsi_dsc_rc_buf_thresh[i]; + drm_dsc_set_const_params(dsc); + drm_dsc_set_rc_buf_thresh(dsc); - for (i = 0; i < DSC_NUM_BUF_RANGES; i++) { - dsc->rc_range_params[i].range_min_qp = min_qp[i]; - dsc->rc_range_params[i].range_max_qp = max_qp[i]; - /* - * Range BPG Offset contains two's-complement signed values that fill - * 8 bits, yet the registers and DCS PPS field are only 6 bits wide. - */ - dsc->rc_range_params[i].range_bpg_offset = bpg_offset[i] & DSC_RANGE_BPG_OFFSET_MASK; + /* handle only bpp = bpc = 8, pre-SCR panels */ + ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); + if (ret) { + DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n"); + return ret; } - dsc->initial_offset = 6144; /* Not bpp 12 */ - if (bpp != 8) - dsc->initial_offset = 2048; /* bpp = 12 */ - - if (dsc->bits_per_component <= 10) - dsc->mux_word_size = DSC_MUX_WORD_SIZE_8_10_BPC; - else - dsc->mux_word_size = DSC_MUX_WORD_SIZE_12_BPC; - - dsc->initial_xmit_delay = 512; - dsc->initial_scale_value = 32; - dsc->first_line_bpg_offset = 12; + dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); dsc->line_buf_depth = dsc->bits_per_component + 1; - /* bpc 8 */ - dsc->flatness_min_qp = 3; - dsc->flatness_max_qp = 12; - dsc->rc_quant_incr_limit0 = 11; - dsc->rc_quant_incr_limit1 = 11; - return drm_dsc_compute_rc_parameters(dsc); } @@ -2417,7 +2358,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, dsi_timing_setup(msm_host, is_bonded_dsi); dsi_sw_reset(msm_host); - dsi_ctrl_config(msm_host, true, phy_shared_timings, phy); + dsi_ctrl_enable(msm_host, phy_shared_timings, phy); if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 1); @@ -2449,7 +2390,7 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host) goto unlock_ret; } - dsi_ctrl_config(msm_host, false, NULL, NULL); + dsi_ctrl_disable(msm_host); if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 0); diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 1bbac72dad35..28b8012a21f2 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -34,32 +34,6 @@ static struct msm_dsi_manager msm_dsim_glb; #define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed) #define IS_MASTER_DSI_LINK(id) (msm_dsim_glb.master_dsi_link_id == id) -#ifdef CONFIG_OF -static bool dsi_mgr_power_on_early(struct drm_bridge *bridge) -{ - struct drm_bridge *next_bridge = drm_bridge_get_next_bridge(bridge); - - /* - * If the next bridge in the chain is the Parade ps8640 bridge chip - * then don't power on early since it seems to violate the expectations - * of the firmware that the bridge chip is running. - * - * NOTE: this is expected to be a temporary special case. It's expected - * that we'll eventually have a framework that allows the next level - * bridge to indicate whether it needs us to power on before it or - * after it. When that framework is in place then we'll use it and - * remove this special case. - */ - return !(next_bridge && next_bridge->of_node && - of_device_is_compatible(next_bridge->of_node, "parade,ps8640")); -} -#else -static inline bool dsi_mgr_power_on_early(struct drm_bridge *bridge) -{ - return true; -} -#endif - static inline struct msm_dsi *dsi_mgr_get_dsi(int id) { return msm_dsim_glb.dsi[id]; @@ -254,7 +228,7 @@ static void msm_dsi_manager_set_split_display(u8 id) } } -static void dsi_mgr_bridge_power_on(struct drm_bridge *bridge) +static int dsi_mgr_bridge_power_on(struct drm_bridge *bridge) { int id = dsi_mgr_bridge_get_id(bridge); struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); @@ -265,12 +239,6 @@ static void dsi_mgr_bridge_power_on(struct drm_bridge *bridge) int ret; DBG("id=%d", id); - if (!msm_dsi_device_connected(msm_dsi)) - return; - - /* Do nothing with the host if it is slave-DSI in case of bonded DSI */ - if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) - return; ret = dsi_mgr_phy_enable(id, phy_shared_timings); if (ret) @@ -300,14 +268,31 @@ static void dsi_mgr_bridge_power_on(struct drm_bridge *bridge) if (is_bonded_dsi && msm_dsi1) msm_dsi_host_enable_irq(msm_dsi1->host); - return; + return 0; host1_on_fail: msm_dsi_host_power_off(host); host_on_fail: dsi_mgr_phy_disable(id); phy_en_fail: - return; + return ret; +} + +static void dsi_mgr_bridge_power_off(struct drm_bridge *bridge) +{ + int id = dsi_mgr_bridge_get_id(bridge); + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); + struct mipi_dsi_host *host = msm_dsi->host; + bool is_bonded_dsi = IS_BONDED_DSI(); + + msm_dsi_host_disable_irq(host); + if (is_bonded_dsi && msm_dsi1) { + msm_dsi_host_disable_irq(msm_dsi1->host); + msm_dsi_host_power_off(msm_dsi1->host); + } + msm_dsi_host_power_off(host); + dsi_mgr_phy_disable(id); } static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) @@ -327,8 +312,11 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) return; - if (!dsi_mgr_power_on_early(bridge)) - dsi_mgr_bridge_power_on(bridge); + ret = dsi_mgr_bridge_power_on(bridge); + if (ret) { + dev_err(&msm_dsi->pdev->dev, "Power on failed: %d\n", ret); + return; + } ret = msm_dsi_host_enable(host); if (ret) { @@ -349,8 +337,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) host1_en_fail: msm_dsi_host_disable(host); host_en_fail: - - return; + dsi_mgr_bridge_power_off(bridge); } void msm_dsi_manager_tpg_enable(void) @@ -438,9 +425,6 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, msm_dsi_host_set_display_mode(host, adjusted_mode); if (is_bonded_dsi && other_dsi) msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode); - - if (dsi_mgr_power_on_early(bridge)) - dsi_mgr_bridge_power_on(bridge); } static enum drm_mode_status dsi_mgr_bridge_mode_valid(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index bb09cbe8ff86..9d5795c58a98 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -541,6 +541,8 @@ static const struct of_device_id dsi_phy_dt_match[] = { .data = &dsi_phy_28nm_hpm_famb_cfgs }, { .compatible = "qcom,dsi-phy-28nm-lp", .data = &dsi_phy_28nm_lp_cfgs }, + { .compatible = "qcom,dsi-phy-28nm-8226", + .data = &dsi_phy_28nm_8226_cfgs }, #endif #ifdef CONFIG_DRM_MSM_DSI_20NM_PHY { .compatible = "qcom,dsi-phy-20nm", diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index 7137a17ae523..8b640d174785 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -46,8 +46,9 @@ struct msm_dsi_phy_cfg { extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_famb_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs; -extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8226_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_14nm_2290_cfgs; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c index 9f488adea7f5..3ce45b023e63 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c @@ -539,6 +539,9 @@ static int dsi_pll_14nm_vco_prepare(struct clk_hw *hw) if (unlikely(pll_14nm->phy->pll_on)) return 0; + if (dsi_pll_14nm_vco_recalc_rate(hw, VCO_REF_CLK_RATE) == 0) + dsi_pll_14nm_vco_set_rate(hw, pll_14nm->phy->cfg->min_pll_rate, VCO_REF_CLK_RATE); + dsi_phy_write(base + REG_DSI_14nm_PHY_PLL_VREF_CFG1, 0x10); dsi_phy_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 1); diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c index 4c1bf55c5f38..ceec7bb87bf1 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -37,6 +37,7 @@ /* v2.0.0 28nm LP implementation */ #define DSI_PHY_28NM_QUIRK_PHY_LP BIT(0) +#define DSI_PHY_28NM_QUIRK_PHY_8226 BIT(1) #define LPFR_LUT_SIZE 10 struct lpfr_cfg { @@ -377,6 +378,74 @@ static int dsi_pll_28nm_vco_prepare_hpm(struct clk_hw *hw) return ret; } +static int dsi_pll_28nm_vco_prepare_8226(struct clk_hw *hw) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw); + struct device *dev = &pll_28nm->phy->pdev->dev; + void __iomem *base = pll_28nm->phy->pll_base; + u32 max_reads = 5, timeout_us = 100; + bool locked; + u32 val; + int i; + + DBG("id=%d", pll_28nm->phy->id); + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + dsi_phy_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34); + + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + + for (i = 0; i < 7; i++) { + /* DSI Uniphy lock detect setting */ + dsi_phy_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, + 0x0c, 100); + dsi_phy_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + + /* poll for PLL ready status */ + locked = pll_28nm_poll_for_ready(pll_28nm, + max_reads, timeout_us); + if (locked) + break; + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00, 50); + + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 100); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + dsi_phy_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + } + + if (unlikely(!locked)) + DRM_DEV_ERROR(dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL Lock success"); + + return locked ? 0 : -EINVAL; +} + static int dsi_pll_28nm_vco_prepare_lp(struct clk_hw *hw) { struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw); @@ -471,6 +540,15 @@ static const struct clk_ops clk_ops_dsi_pll_28nm_vco_lp = { .is_enabled = dsi_pll_28nm_clk_is_enabled, }; +static const struct clk_ops clk_ops_dsi_pll_28nm_vco_8226 = { + .round_rate = dsi_pll_28nm_clk_round_rate, + .set_rate = dsi_pll_28nm_clk_set_rate, + .recalc_rate = dsi_pll_28nm_clk_recalc_rate, + .prepare = dsi_pll_28nm_vco_prepare_8226, + .unprepare = dsi_pll_28nm_vco_unprepare, + .is_enabled = dsi_pll_28nm_clk_is_enabled, +}; + /* * PLL Callbacks */ @@ -536,6 +614,8 @@ static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm, struct clk_hw **prov if (pll_28nm->phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_LP) vco_init.ops = &clk_ops_dsi_pll_28nm_vco_lp; + else if (pll_28nm->phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_8226) + vco_init.ops = &clk_ops_dsi_pll_28nm_vco_8226; else vco_init.ops = &clk_ops_dsi_pll_28nm_vco_hpm; @@ -820,3 +900,20 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { .quirks = DSI_PHY_28NM_QUIRK_PHY_LP, }; +const struct msm_dsi_phy_cfg dsi_phy_28nm_8226_cfgs = { + .has_phy_regulator = true, + .regulator_data = dsi_phy_28nm_regulators, + .num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators), + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + .pll_init = dsi_pll_28nm_init, + .save_pll_state = dsi_28nm_pll_save_state, + .restore_pll_state = dsi_28nm_pll_restore_state, + }, + .min_pll_rate = VCO_MIN_RATE, + .max_pll_rate = VCO_MAX_RATE, + .io_start = { 0xfd922b00 }, + .num_dsi_phy = 1, + .quirks = DSI_PHY_28NM_QUIRK_PHY_8226, +}; |