225 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright © 2023 Intel Corporation4 */5 6#include <drm/drm_atomic.h>7#include <drm/drm_atomic_helper.h>8#include <drm/drm_atomic_uapi.h>9 10#include "i915_drv.h"11#include "intel_atomic.h"12#include "intel_crtc.h"13#include "intel_display_types.h"14#include "intel_load_detect.h"15 16/* VESA 640x480x72Hz mode to set on the pipe */17static const struct drm_display_mode load_detect_mode = {18 DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,19 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),20};21 22static int intel_modeset_disable_planes(struct drm_atomic_state *state,23 struct drm_crtc *crtc)24{25 struct drm_plane *plane;26 struct drm_plane_state *plane_state;27 int ret, i;28 29 ret = drm_atomic_add_affected_planes(state, crtc);30 if (ret)31 return ret;32 33 for_each_new_plane_in_state(state, plane, plane_state, i) {34 if (plane_state->crtc != crtc)35 continue;36 37 ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);38 if (ret)39 return ret;40 41 drm_atomic_set_fb_for_plane(plane_state, NULL);42 }43 44 return 0;45}46 47struct drm_atomic_state *48intel_load_detect_get_pipe(struct drm_connector *connector,49 struct drm_modeset_acquire_ctx *ctx)50{51 struct intel_display *display = to_intel_display(connector->dev);52 struct intel_encoder *encoder =53 intel_attached_encoder(to_intel_connector(connector));54 struct intel_crtc *possible_crtc;55 struct intel_crtc *crtc = NULL;56 struct drm_mode_config *config = &display->drm->mode_config;57 struct drm_atomic_state *state = NULL, *restore_state = NULL;58 struct drm_connector_state *connector_state;59 struct intel_crtc_state *crtc_state;60 int ret;61 62 drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",63 connector->base.id, connector->name,64 encoder->base.base.id, encoder->base.name);65 66 drm_WARN_ON(display->drm, !drm_modeset_is_locked(&config->connection_mutex));67 68 /*69 * Algorithm gets a little messy:70 *71 * - if the connector already has an assigned crtc, use it (but make72 * sure it's on first)73 *74 * - try to find the first unused crtc that can drive this connector,75 * and use that if we find one76 */77 78 /* See if we already have a CRTC for this connector */79 if (connector->state->crtc) {80 crtc = to_intel_crtc(connector->state->crtc);81 82 ret = drm_modeset_lock(&crtc->base.mutex, ctx);83 if (ret)84 goto fail;85 86 /* Make sure the crtc and connector are running */87 goto found;88 }89 90 /* Find an unused one (if possible) */91 for_each_intel_crtc(display->drm, possible_crtc) {92 if (!(encoder->base.possible_crtcs &93 drm_crtc_mask(&possible_crtc->base)))94 continue;95 96 ret = drm_modeset_lock(&possible_crtc->base.mutex, ctx);97 if (ret)98 goto fail;99 100 if (possible_crtc->base.state->enable) {101 drm_modeset_unlock(&possible_crtc->base.mutex);102 continue;103 }104 105 crtc = possible_crtc;106 break;107 }108 109 /*110 * If we didn't find an unused CRTC, don't use any.111 */112 if (!crtc) {113 drm_dbg_kms(display->drm,114 "no pipe available for load-detect\n");115 ret = -ENODEV;116 goto fail;117 }118 119found:120 state = drm_atomic_state_alloc(display->drm);121 restore_state = drm_atomic_state_alloc(display->drm);122 if (!state || !restore_state) {123 ret = -ENOMEM;124 goto fail;125 }126 127 state->acquire_ctx = ctx;128 to_intel_atomic_state(state)->internal = true;129 130 restore_state->acquire_ctx = ctx;131 to_intel_atomic_state(restore_state)->internal = true;132 133 connector_state = drm_atomic_get_connector_state(state, connector);134 if (IS_ERR(connector_state)) {135 ret = PTR_ERR(connector_state);136 goto fail;137 }138 139 ret = drm_atomic_set_crtc_for_connector(connector_state, &crtc->base);140 if (ret)141 goto fail;142 143 crtc_state = intel_atomic_get_crtc_state(state, crtc);144 if (IS_ERR(crtc_state)) {145 ret = PTR_ERR(crtc_state);146 goto fail;147 }148 149 crtc_state->uapi.active = true;150 151 ret = drm_atomic_set_mode_for_crtc(&crtc_state->uapi,152 &load_detect_mode);153 if (ret)154 goto fail;155 156 ret = intel_modeset_disable_planes(state, &crtc->base);157 if (ret)158 goto fail;159 160 ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(restore_state, connector));161 if (!ret)162 ret = PTR_ERR_OR_ZERO(drm_atomic_get_crtc_state(restore_state, &crtc->base));163 if (!ret)164 ret = drm_atomic_add_affected_planes(restore_state, &crtc->base);165 if (ret) {166 drm_dbg_kms(display->drm,167 "Failed to create a copy of old state to restore: %i\n",168 ret);169 goto fail;170 }171 172 ret = drm_atomic_commit(state);173 if (ret) {174 drm_dbg_kms(display->drm,175 "failed to set mode on load-detect pipe\n");176 goto fail;177 }178 179 drm_atomic_state_put(state);180 181 /* let the connector get through one full cycle before testing */182 intel_crtc_wait_for_next_vblank(crtc);183 184 return restore_state;185 186fail:187 if (state) {188 drm_atomic_state_put(state);189 state = NULL;190 }191 if (restore_state) {192 drm_atomic_state_put(restore_state);193 restore_state = NULL;194 }195 196 if (ret == -EDEADLK)197 return ERR_PTR(ret);198 199 return NULL;200}201 202void intel_load_detect_release_pipe(struct drm_connector *connector,203 struct drm_atomic_state *state,204 struct drm_modeset_acquire_ctx *ctx)205{206 struct intel_display *display = to_intel_display(connector->dev);207 struct intel_encoder *intel_encoder =208 intel_attached_encoder(to_intel_connector(connector));209 struct drm_encoder *encoder = &intel_encoder->base;210 int ret;211 212 drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",213 connector->base.id, connector->name,214 encoder->base.id, encoder->name);215 216 if (IS_ERR_OR_NULL(state))217 return;218 219 ret = drm_atomic_helper_commit_duplicated_state(state, ctx);220 if (ret)221 drm_dbg_kms(display->drm,222 "Couldn't release load detect pipe: %i\n", ret);223 drm_atomic_state_put(state);224}225