1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2015, The Linux Foundation. All rights reserved. 4 */ 5 6 #include <drm/drm_crtc.h> 7 #include <drm/drm_probe_helper.h> 8 9 #include "mdp5_kms.h" 10 11 static struct mdp5_kms *get_kms(struct drm_encoder *encoder) 12 { 13 struct msm_drm_private *priv = encoder->dev->dev_private; 14 return to_mdp5_kms(to_mdp_kms(priv->kms)); 15 } 16 17 #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING 18 #include <mach/board.h> 19 #include <linux/msm-bus.h> 20 #include <linux/msm-bus-board.h> 21 22 static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx) 23 { 24 if (mdp5_cmd_enc->bsc) { 25 DBG("set bus scaling: %d", idx); 26 /* HACK: scaling down, and then immediately back up 27 * seems to leave things broken (underflow).. so 28 * never disable: 29 */ 30 idx = 1; 31 msm_bus_scale_client_update_request(mdp5_cmd_enc->bsc, idx); 32 } 33 } 34 #else 35 static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx) {} 36 #endif 37 38 #define VSYNC_CLK_RATE 19200000 39 static int pingpong_tearcheck_setup(struct drm_encoder *encoder, 40 struct drm_display_mode *mode) 41 { 42 struct mdp5_kms *mdp5_kms = get_kms(encoder); 43 struct device *dev = encoder->dev->dev; 44 u32 total_lines_x100, vclks_line, cfg; 45 long vsync_clk_speed; 46 struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); 47 int pp_id = mixer->pp; 48 49 if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) { 50 DRM_DEV_ERROR(dev, "vsync_clk is not initialized\n"); 51 return -EINVAL; 52 } 53 54 total_lines_x100 = mode->vtotal * drm_mode_vrefresh(mode); 55 if (!total_lines_x100) { 56 DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n", 57 __func__, mode->vtotal, drm_mode_vrefresh(mode)); 58 return -EINVAL; 59 } 60 61 vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE); 62 if (vsync_clk_speed <= 0) { 63 DRM_DEV_ERROR(dev, "vsync_clk round rate failed %ld\n", 64 vsync_clk_speed); 65 return -EINVAL; 66 } 67 vclks_line = vsync_clk_speed * 100 / total_lines_x100; 68 69 cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN 70 | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN; 71 cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line); 72 73 mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg); 74 mdp5_write(mdp5_kms, 75 REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0); 76 mdp5_write(mdp5_kms, 77 REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay); 78 mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1); 79 mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay); 80 mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id), 81 MDP5_PP_SYNC_THRESH_START(4) | 82 MDP5_PP_SYNC_THRESH_CONTINUE(4)); 83 84 return 0; 85 } 86 87 static int pingpong_tearcheck_enable(struct drm_encoder *encoder) 88 { 89 struct mdp5_kms *mdp5_kms = get_kms(encoder); 90 struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); 91 int pp_id = mixer->pp; 92 int ret; 93 94 ret = clk_set_rate(mdp5_kms->vsync_clk, 95 clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE)); 96 if (ret) { 97 DRM_DEV_ERROR(encoder->dev->dev, 98 "vsync_clk clk_set_rate failed, %d\n", ret); 99 return ret; 100 } 101 ret = clk_prepare_enable(mdp5_kms->vsync_clk); 102 if (ret) { 103 DRM_DEV_ERROR(encoder->dev->dev, 104 "vsync_clk clk_prepare_enable failed, %d\n", ret); 105 return ret; 106 } 107 108 mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1); 109 110 return 0; 111 } 112 113 static void pingpong_tearcheck_disable(struct drm_encoder *encoder) 114 { 115 struct mdp5_kms *mdp5_kms = get_kms(encoder); 116 struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); 117 int pp_id = mixer->pp; 118 119 mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); 120 clk_disable_unprepare(mdp5_kms->vsync_clk); 121 } 122 123 void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, 124 struct drm_display_mode *mode, 125 struct drm_display_mode *adjusted_mode) 126 { 127 mode = adjusted_mode; 128 129 DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode)); 130 pingpong_tearcheck_setup(encoder, mode); 131 mdp5_crtc_set_pipeline(encoder->crtc); 132 } 133 134 void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) 135 { 136 struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); 137 struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; 138 struct mdp5_interface *intf = mdp5_cmd_enc->intf; 139 struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); 140 141 if (WARN_ON(!mdp5_cmd_enc->enabled)) 142 return; 143 144 pingpong_tearcheck_disable(encoder); 145 146 mdp5_ctl_set_encoder_state(ctl, pipeline, false); 147 mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true); 148 149 bs_set(mdp5_cmd_enc, 0); 150 151 mdp5_cmd_enc->enabled = false; 152 } 153 154 void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) 155 { 156 struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); 157 struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; 158 struct mdp5_interface *intf = mdp5_cmd_enc->intf; 159 struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); 160 161 if (WARN_ON(mdp5_cmd_enc->enabled)) 162 return; 163 164 bs_set(mdp5_cmd_enc, 1); 165 if (pingpong_tearcheck_enable(encoder)) 166 return; 167 168 mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true); 169 170 mdp5_ctl_set_encoder_state(ctl, pipeline, true); 171 172 mdp5_cmd_enc->enabled = true; 173 } 174 175 int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, 176 struct drm_encoder *slave_encoder) 177 { 178 struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); 179 struct mdp5_kms *mdp5_kms; 180 struct device *dev; 181 int intf_num; 182 u32 data = 0; 183 184 if (!encoder || !slave_encoder) 185 return -EINVAL; 186 187 mdp5_kms = get_kms(encoder); 188 intf_num = mdp5_cmd_enc->intf->num; 189 190 /* Switch slave encoder's trigger MUX, to use the master's 191 * start signal for the slave encoder 192 */ 193 if (intf_num == 1) 194 data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX; 195 else if (intf_num == 2) 196 data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX; 197 else 198 return -EINVAL; 199 200 /* Smart Panel, Sync mode */ 201 data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL; 202 203 dev = &mdp5_kms->pdev->dev; 204 205 /* Make sure clocks are on when connectors calling this function. */ 206 pm_runtime_get_sync(dev); 207 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data); 208 209 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, 210 MDP5_SPLIT_DPL_LOWER_SMART_PANEL); 211 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); 212 pm_runtime_put_sync(dev); 213 214 return 0; 215 } 216