Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_crtc.h> #include "msm_drv.h" #include "msm_kms.h" #include "dp_drm.h" /** * dp_bridge_detect - callback to determine if connector is connected * @bridge: Pointer to drm bridge structure * Returns: Bridge's 'is connected' status */ static enum drm_connector_status dp_bridge_detect(struct drm_bridge *bridge) { struct msm_dp *dp; dp = to_dp_bridge(bridge)->dp_display; drm_dbg_dp(dp->drm_dev, "is_connected = %s\n", (dp->is_connected) ? "true" : "false"); return (dp->is_connected) ? connector_status_connected : connector_status_disconnected; } static int dp_bridge_atomic_check(struct drm_bridge *bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct msm_dp *dp; dp = to_dp_bridge(bridge)->dp_display; drm_dbg_dp(dp->drm_dev, "is_connected = %s\n", (dp->is_connected) ? "true" : "false"); /* * There is no protection in the DRM framework to check if the display * pipeline has been already disabled before trying to disable it again. * Hence if the sink is unplugged, the pipeline gets disabled, but the * crtc->active is still true. Any attempt to set the mode or manually * disable this encoder will result in the crash. * * TODO: add support for telling the DRM subsystem that the pipeline is * disabled by the hardware and thus all access to it should be forbidden. * After that this piece of code can be removed. */ if (bridge->ops & DRM_BRIDGE_OP_HPD) return (dp->is_connected) ? 0 : -ENOTCONN; return 0; } /** * dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add() * @bridge: Poiner to drm bridge * @connector: Pointer to drm connector structure * Returns: Number of modes added */ static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector) { int rc = 0; struct msm_dp *dp; if (!connector) return 0; dp = to_dp_bridge(bridge)->dp_display; /* pluggable case assumes EDID is read when HPD */ if (dp->is_connected) { rc = dp_display_get_modes(dp); if (rc <= 0) { DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc); return rc; } } else { drm_dbg_dp(connector->dev, "No sink connected\n"); } return rc; } static const struct drm_bridge_funcs dp_bridge_ops = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_enable = dp_bridge_atomic_enable, .atomic_disable = dp_bridge_atomic_disable, .atomic_post_disable = dp_bridge_atomic_post_disable, .mode_set = dp_bridge_mode_set, .mode_valid = dp_bridge_mode_valid, .get_modes = dp_bridge_get_modes, .detect = dp_bridge_detect, .atomic_check = dp_bridge_atomic_check, .hpd_enable = dp_bridge_hpd_enable, .hpd_disable = dp_bridge_hpd_disable, .hpd_notify = dp_bridge_hpd_notify, }; static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct msm_dp *dp = to_dp_bridge(drm_bridge)->dp_display; if (WARN_ON(!conn_state)) return -ENODEV; conn_state->self_refresh_aware = dp->psr_supported; if (!conn_state->crtc || !crtc_state) return 0; if (crtc_state->self_refresh_active && !dp->psr_supported) return -EINVAL; return 0; } static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_bridge_state *old_bridge_state) { struct drm_atomic_state *atomic_state = old_bridge_state->base.state; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); struct msm_dp *dp = dp_bridge->dp_display; /* * Check the old state of the crtc to determine if the panel * was put into psr state previously by the edp_bridge_atomic_disable. * If the panel is in psr, just exit psr state and skip the full * bridge enable sequence. */ crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, drm_bridge->encoder); if (!crtc) return; old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); if (old_crtc_state && old_crtc_state->self_refresh_active) { dp_display_set_psr(dp, false); return; } dp_bridge_atomic_enable(drm_bridge, old_bridge_state); } static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge, struct drm_bridge_state *old_bridge_state) { struct drm_atomic_state *atomic_state = old_bridge_state->base.state; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state = NULL; struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); struct msm_dp *dp = dp_bridge->dp_display; crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, drm_bridge->encoder); if (!crtc) goto out; new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); if (!new_crtc_state) goto out; old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); if (!old_crtc_state) goto out; /* * Set self refresh mode if current crtc state is active. * * If old crtc state is active, then this is a display disable * call while the sink is in psr state. So, exit psr here. * The eDP controller will be disabled in the * edp_bridge_atomic_post_disable function. * * We observed sink is stuck in self refresh if psr exit is skipped * when display disable occurs while the sink is in psr state. */ if (new_crtc_state->self_refresh_active) { dp_display_set_psr(dp, true); return; } else if (old_crtc_state->self_refresh_active) { dp_display_set_psr(dp, false); return; } out: dp_bridge_atomic_disable(drm_bridge, old_bridge_state); } static void edp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, struct drm_bridge_state *old_bridge_state) { struct drm_atomic_state *atomic_state = old_bridge_state->base.state; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state = NULL; crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, drm_bridge->encoder); if (!crtc) return; new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); if (!new_crtc_state) return; /* * Self refresh mode is already set in edp_bridge_atomic_disable. */ if (new_crtc_state->self_refresh_active) return; dp_bridge_atomic_post_disable(drm_bridge, old_bridge_state); } /** * edp_bridge_mode_valid - callback to determine if specified mode is valid * @bridge: Pointer to drm bridge structure * @info: display info * @mode: Pointer to drm mode structure * Returns: Validity status for specified mode */ static enum drm_mode_status edp_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) { struct msm_dp *dp; int mode_pclk_khz = mode->clock; dp = to_dp_bridge(bridge)->dp_display; if (!dp || !mode_pclk_khz || !dp->connector) { DRM_ERROR("invalid params\n"); return -EINVAL; } if (mode->clock > DP_MAX_PIXEL_CLK_KHZ) return MODE_CLOCK_HIGH; /* * The eDP controller currently does not have a reliable way of * enabling panel power to read sink capabilities. So, we rely * on the panel driver to populate only supported modes for now. */ return MODE_OK; } static const struct drm_bridge_funcs edp_bridge_ops = { .atomic_enable = edp_bridge_atomic_enable, .atomic_disable = edp_bridge_atomic_disable, .atomic_post_disable = edp_bridge_atomic_post_disable, .mode_set = dp_bridge_mode_set, .mode_valid = edp_bridge_mode_valid, .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_check = edp_bridge_atomic_check, }; struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, struct drm_encoder *encoder) { int rc; struct msm_dp_bridge *dp_bridge; struct drm_bridge *bridge; dp_bridge = devm_kzalloc(dev->dev, sizeof(*dp_bridge), GFP_KERNEL); if (!dp_bridge) return ERR_PTR(-ENOMEM); dp_bridge->dp_display = dp_display; bridge = &dp_bridge->bridge; bridge->funcs = dp_display->is_edp ? &edp_bridge_ops : &dp_bridge_ops; bridge->type = dp_display->connector_type; /* * Many ops only make sense for DP. Why? * - Detect/HPD are used by DRM to know if a display is _physically_ * there, not whether the display is powered on / finished initting. * On eDP we assume the display is always there because you can't * know until power is applied. If we don't implement the ops DRM will * assume our display is always there. * - Currently eDP mode reading is driven by the panel driver. This * allows the panel driver to properly power itself on to read the * modes. */ if (!dp_display->is_edp) { bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES; } drm_bridge_add(bridge); rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (rc) { DRM_ERROR("failed to attach bridge, rc=%d\n", rc); drm_bridge_remove(bridge); return ERR_PTR(rc); } if (dp_display->next_bridge) { rc = drm_bridge_attach(encoder, dp_display->next_bridge, bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (rc < 0) { DRM_ERROR("failed to attach panel bridge: %d\n", rc); drm_bridge_remove(bridge); return ERR_PTR(rc); } } return bridge; } /* connector initialization */ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder) { struct drm_connector *connector = NULL; connector = drm_bridge_connector_init(dp_display->drm_dev, encoder); if (IS_ERR(connector)) return connector; drm_connector_attach_encoder(connector, encoder); return connector; } |