Commit | Line | Data |
---|---|---|
0e32b39c DA |
1 | /* |
2 | * Copyright © 2008 Intel Corporation | |
3 | * 2014 Red Hat Inc. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | * copy of this software and associated documentation files (the "Software"), | |
7 | * to deal in the Software without restriction, including without limitation | |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | * and/or sell copies of the Software, and to permit persons to whom the | |
10 | * Software is furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice (including the next | |
13 | * paragraph) shall be included in all copies or substantial portions of the | |
14 | * Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
22 | * IN THE SOFTWARE. | |
23 | * | |
24 | */ | |
25 | ||
26 | #include <drm/drmP.h> | |
27 | #include "i915_drv.h" | |
28 | #include "intel_drv.h" | |
c6f95f27 | 29 | #include <drm/drm_atomic_helper.h> |
0e32b39c DA |
30 | #include <drm/drm_crtc_helper.h> |
31 | #include <drm/drm_edid.h> | |
32 | ||
33 | static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, | |
0a478c27 ML |
34 | struct intel_crtc_state *pipe_config, |
35 | struct drm_connector_state *conn_state) | |
0e32b39c | 36 | { |
53e9bf5e | 37 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); |
0e32b39c DA |
38 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); |
39 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
40 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
7f9e7754 LY |
41 | struct intel_connector *connector = |
42 | to_intel_connector(conn_state->connector); | |
f424f55e | 43 | struct drm_atomic_state *state = pipe_config->base.state; |
1189e4f4 | 44 | int bpp; |
04a60f9f | 45 | int lane_count, slots; |
7c5f93b0 | 46 | const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; |
0e32b39c | 47 | int mst_pbn; |
b31e85ed JN |
48 | bool reduce_m_n = drm_dp_has_quirk(&intel_dp->desc, |
49 | DP_DPCD_QUIRK_LIMITED_M_N); | |
0e32b39c | 50 | |
541ab84d VS |
51 | if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) |
52 | return false; | |
53 | ||
0e32b39c | 54 | pipe_config->has_pch_encoder = false; |
0e32b39c | 55 | bpp = 24; |
611032bf MN |
56 | if (intel_dp->compliance.test_data.bpc) { |
57 | bpp = intel_dp->compliance.test_data.bpc * 3; | |
58 | DRM_DEBUG_KMS("Setting pipe bpp to %d\n", | |
59 | bpp); | |
60 | } | |
0e32b39c DA |
61 | /* |
62 | * for MST we always configure max link bw - the spec doesn't | |
63 | * seem to suggest we should do otherwise. | |
64 | */ | |
3d65a735 | 65 | lane_count = intel_dp_max_lane_count(intel_dp); |
ed4e9c1d | 66 | |
90a6b7b0 | 67 | pipe_config->lane_count = lane_count; |
0e32b39c | 68 | |
611032bf | 69 | pipe_config->pipe_bpp = bpp; |
0e32b39c | 70 | |
f424f55e | 71 | pipe_config->port_clock = intel_dp_max_link_rate(intel_dp); |
e75f4771 | 72 | |
7f9e7754 LY |
73 | if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port)) |
74 | pipe_config->has_audio = true; | |
0e32b39c | 75 | |
f424f55e | 76 | mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp); |
0e32b39c | 77 | pipe_config->pbn = mst_pbn; |
f424f55e PD |
78 | |
79 | slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr, | |
80 | connector->port, mst_pbn); | |
81 | if (slots < 0) { | |
82 | DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots); | |
83 | return false; | |
84 | } | |
0e32b39c DA |
85 | |
86 | intel_link_compute_m_n(bpp, lane_count, | |
87 | adjusted_mode->crtc_clock, | |
88 | pipe_config->port_clock, | |
b31e85ed JN |
89 | &pipe_config->dp_m_n, |
90 | reduce_m_n); | |
0e32b39c DA |
91 | |
92 | pipe_config->dp_m_n.tu = slots; | |
6fa2d197 | 93 | |
5161d058 VS |
94 | if (IS_GEN9_LP(dev_priv)) |
95 | pipe_config->lane_lat_optim_mask = | |
96 | bxt_ddi_phy_calc_lane_lat_optim_mask(pipe_config->lane_count); | |
97 | ||
53e9bf5e VS |
98 | intel_ddi_compute_min_voltage_level(dev_priv, pipe_config); |
99 | ||
0e32b39c | 100 | return true; |
f424f55e | 101 | } |
0e32b39c | 102 | |
f424f55e PD |
103 | static int intel_dp_mst_atomic_check(struct drm_connector *connector, |
104 | struct drm_connector_state *new_conn_state) | |
105 | { | |
106 | struct drm_atomic_state *state = new_conn_state->state; | |
107 | struct drm_connector_state *old_conn_state; | |
108 | struct drm_crtc *old_crtc; | |
109 | struct drm_crtc_state *crtc_state; | |
110 | int slots, ret = 0; | |
111 | ||
112 | old_conn_state = drm_atomic_get_old_connector_state(state, connector); | |
113 | old_crtc = old_conn_state->crtc; | |
114 | if (!old_crtc) | |
115 | return ret; | |
116 | ||
117 | crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc); | |
118 | slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu; | |
119 | if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) { | |
120 | struct drm_dp_mst_topology_mgr *mgr; | |
121 | struct drm_encoder *old_encoder; | |
122 | ||
123 | old_encoder = old_conn_state->best_encoder; | |
124 | mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr; | |
125 | ||
126 | ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots); | |
127 | if (ret) | |
128 | DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret); | |
129 | else | |
130 | to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0; | |
131 | } | |
132 | return ret; | |
0e32b39c DA |
133 | } |
134 | ||
fd6bbda9 | 135 | static void intel_mst_disable_dp(struct intel_encoder *encoder, |
5f88a9c6 VS |
136 | const struct intel_crtc_state *old_crtc_state, |
137 | const struct drm_connector_state *old_conn_state) | |
0e32b39c DA |
138 | { |
139 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
140 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
141 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
1e7bfa0b ML |
142 | struct intel_connector *connector = |
143 | to_intel_connector(old_conn_state->connector); | |
0e32b39c DA |
144 | int ret; |
145 | ||
9b1c5818 | 146 | DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links); |
0e32b39c | 147 | |
1e7bfa0b | 148 | drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port); |
0e32b39c DA |
149 | |
150 | ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); | |
151 | if (ret) { | |
152 | DRM_ERROR("failed to update payload %d\n", ret); | |
153 | } | |
37255d8d | 154 | if (old_crtc_state->has_audio) |
8ec47de2 VS |
155 | intel_audio_codec_disable(encoder, |
156 | old_crtc_state, old_conn_state); | |
0e32b39c DA |
157 | } |
158 | ||
fd6bbda9 | 159 | static void intel_mst_post_disable_dp(struct intel_encoder *encoder, |
5f88a9c6 VS |
160 | const struct intel_crtc_state *old_crtc_state, |
161 | const struct drm_connector_state *old_conn_state) | |
0e32b39c DA |
162 | { |
163 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
164 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
165 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
1e7bfa0b ML |
166 | struct intel_connector *connector = |
167 | to_intel_connector(old_conn_state->connector); | |
0e32b39c | 168 | |
0e32b39c DA |
169 | /* this can fail */ |
170 | drm_dp_check_act_status(&intel_dp->mst_mgr); | |
171 | /* and this can also fail */ | |
172 | drm_dp_update_payload_part2(&intel_dp->mst_mgr); | |
173 | ||
1e7bfa0b | 174 | drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port); |
0e32b39c | 175 | |
5ea2355a DP |
176 | /* |
177 | * Power down mst path before disabling the port, otherwise we end | |
178 | * up getting interrupts from the sink upon detecting link loss. | |
179 | */ | |
180 | drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, | |
181 | false); | |
182 | ||
19e0b4ca | 183 | intel_dp->active_mst_links--; |
0552f765 DA |
184 | |
185 | intel_mst->connector = NULL; | |
be1c63c8 LP |
186 | if (intel_dp->active_mst_links == 0) { |
187 | intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); | |
fd6bbda9 | 188 | intel_dig_port->base.post_disable(&intel_dig_port->base, |
1939ba51 | 189 | old_crtc_state, NULL); |
be1c63c8 | 190 | } |
1939ba51 | 191 | |
9b1c5818 | 192 | DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links); |
0e32b39c DA |
193 | } |
194 | ||
5161d058 VS |
195 | static void intel_mst_pre_pll_enable_dp(struct intel_encoder *encoder, |
196 | const struct intel_crtc_state *pipe_config, | |
197 | const struct drm_connector_state *conn_state) | |
198 | { | |
199 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
200 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
201 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
202 | ||
203 | if (intel_dp->active_mst_links == 0 && | |
204 | intel_dig_port->base.pre_pll_enable) | |
205 | intel_dig_port->base.pre_pll_enable(&intel_dig_port->base, | |
206 | pipe_config, NULL); | |
207 | } | |
208 | ||
fd6bbda9 | 209 | static void intel_mst_pre_enable_dp(struct intel_encoder *encoder, |
5f88a9c6 VS |
210 | const struct intel_crtc_state *pipe_config, |
211 | const struct drm_connector_state *conn_state) | |
0e32b39c DA |
212 | { |
213 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
214 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
215 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
1e7bfa0b | 216 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); |
8f4f2797 | 217 | enum port port = intel_dig_port->base.port; |
1e7bfa0b ML |
218 | struct intel_connector *connector = |
219 | to_intel_connector(conn_state->connector); | |
0e32b39c DA |
220 | int ret; |
221 | uint32_t temp; | |
0e32b39c | 222 | |
e85376cb ML |
223 | /* MST encoders are bound to a crtc, not to a connector, |
224 | * force the mapping here for get_hw_state. | |
225 | */ | |
1e7bfa0b ML |
226 | connector->encoder = encoder; |
227 | intel_mst->connector = connector; | |
e85376cb | 228 | |
9b1c5818 | 229 | DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links); |
0552f765 | 230 | |
be1c63c8 LP |
231 | if (intel_dp->active_mst_links == 0) |
232 | intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); | |
233 | ||
5ea2355a | 234 | drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true); |
be1c63c8 | 235 | |
e081c846 ACO |
236 | if (intel_dp->active_mst_links == 0) |
237 | intel_dig_port->base.pre_enable(&intel_dig_port->base, | |
238 | pipe_config, NULL); | |
0e32b39c DA |
239 | |
240 | ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr, | |
1e7bfa0b | 241 | connector->port, |
1e797f55 PD |
242 | pipe_config->pbn, |
243 | pipe_config->dp_m_n.tu); | |
0e32b39c DA |
244 | if (ret == false) { |
245 | DRM_ERROR("failed to allocate vcpi\n"); | |
246 | return; | |
247 | } | |
248 | ||
249 | ||
19e0b4ca | 250 | intel_dp->active_mst_links++; |
0e32b39c DA |
251 | temp = I915_READ(DP_TP_STATUS(port)); |
252 | I915_WRITE(DP_TP_STATUS(port), temp); | |
253 | ||
254 | ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); | |
255 | } | |
256 | ||
fd6bbda9 | 257 | static void intel_mst_enable_dp(struct intel_encoder *encoder, |
5f88a9c6 VS |
258 | const struct intel_crtc_state *pipe_config, |
259 | const struct drm_connector_state *conn_state) | |
0e32b39c DA |
260 | { |
261 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
262 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
263 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
1e7bfa0b | 264 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); |
8f4f2797 | 265 | enum port port = intel_dig_port->base.port; |
0e32b39c DA |
266 | int ret; |
267 | ||
9b1c5818 | 268 | DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links); |
0e32b39c | 269 | |
3016a31f CW |
270 | if (intel_wait_for_register(dev_priv, |
271 | DP_TP_STATUS(port), | |
272 | DP_TP_STATUS_ACT_SENT, | |
273 | DP_TP_STATUS_ACT_SENT, | |
274 | 1)) | |
0e32b39c DA |
275 | DRM_ERROR("Timed out waiting for ACT sent\n"); |
276 | ||
277 | ret = drm_dp_check_act_status(&intel_dp->mst_mgr); | |
278 | ||
279 | ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr); | |
37255d8d | 280 | if (pipe_config->has_audio) |
7f9e7754 | 281 | intel_audio_codec_enable(encoder, pipe_config, conn_state); |
0e32b39c DA |
282 | } |
283 | ||
284 | static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder, | |
285 | enum pipe *pipe) | |
286 | { | |
287 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
288 | *pipe = intel_mst->pipe; | |
0552f765 | 289 | if (intel_mst->connector) |
0e32b39c DA |
290 | return true; |
291 | return false; | |
292 | } | |
293 | ||
294 | static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder, | |
5cec258b | 295 | struct intel_crtc_state *pipe_config) |
0e32b39c DA |
296 | { |
297 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); | |
298 | struct intel_digital_port *intel_dig_port = intel_mst->primary; | |
53e9bf5e | 299 | |
35686a44 | 300 | intel_ddi_get_config(&intel_dig_port->base, pipe_config); |
0e32b39c DA |
301 | } |
302 | ||
303 | static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector) | |
304 | { | |
305 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
306 | struct intel_dp *intel_dp = intel_connector->mst_port; | |
307 | struct edid *edid; | |
308 | int ret; | |
309 | ||
0552f765 DA |
310 | if (!intel_dp) { |
311 | return intel_connector_update_modes(connector, NULL); | |
312 | } | |
0e32b39c | 313 | |
0552f765 | 314 | edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port); |
0e32b39c DA |
315 | ret = intel_connector_update_modes(connector, edid); |
316 | kfree(edid); | |
317 | ||
318 | return ret; | |
319 | } | |
320 | ||
321 | static enum drm_connector_status | |
f7f3d48a | 322 | intel_dp_mst_detect(struct drm_connector *connector, bool force) |
0e32b39c DA |
323 | { |
324 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
325 | struct intel_dp *intel_dp = intel_connector->mst_port; | |
326 | ||
0552f765 DA |
327 | if (!intel_dp) |
328 | return connector_status_disconnected; | |
c6a0aed4 | 329 | return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr, intel_connector->port); |
0e32b39c DA |
330 | } |
331 | ||
0e32b39c DA |
332 | static void |
333 | intel_dp_mst_connector_destroy(struct drm_connector *connector) | |
334 | { | |
335 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
336 | ||
337 | if (!IS_ERR_OR_NULL(intel_connector->edid)) | |
338 | kfree(intel_connector->edid); | |
339 | ||
340 | drm_connector_cleanup(connector); | |
341 | kfree(connector); | |
342 | } | |
343 | ||
344 | static const struct drm_connector_funcs intel_dp_mst_connector_funcs = { | |
0e32b39c DA |
345 | .detect = intel_dp_mst_detect, |
346 | .fill_modes = drm_helper_probe_single_connector_modes, | |
1ebaa0b9 | 347 | .late_register = intel_connector_register, |
c191eca1 | 348 | .early_unregister = intel_connector_unregister, |
0e32b39c | 349 | .destroy = intel_dp_mst_connector_destroy, |
c6f95f27 | 350 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
98969725 | 351 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
0e32b39c DA |
352 | }; |
353 | ||
354 | static int intel_dp_mst_get_modes(struct drm_connector *connector) | |
355 | { | |
356 | return intel_dp_mst_get_ddc_modes(connector); | |
357 | } | |
358 | ||
359 | static enum drm_mode_status | |
360 | intel_dp_mst_mode_valid(struct drm_connector *connector, | |
361 | struct drm_display_mode *mode) | |
362 | { | |
22a2c8e0 DP |
363 | struct intel_connector *intel_connector = to_intel_connector(connector); |
364 | struct intel_dp *intel_dp = intel_connector->mst_port; | |
832d5bfd | 365 | int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; |
22a2c8e0 DP |
366 | int bpp = 24; /* MST uses fixed bpp */ |
367 | int max_rate, mode_rate, max_lanes, max_link_clock; | |
368 | ||
06bfe5b0 RV |
369 | if (!intel_dp) |
370 | return MODE_ERROR; | |
371 | ||
541ab84d VS |
372 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) |
373 | return MODE_NO_DBLESCAN; | |
374 | ||
22a2c8e0 | 375 | max_link_clock = intel_dp_max_link_rate(intel_dp); |
3d65a735 | 376 | max_lanes = intel_dp_max_lane_count(intel_dp); |
22a2c8e0 DP |
377 | |
378 | max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); | |
379 | mode_rate = intel_dp_link_required(mode->clock, bpp); | |
832d5bfd | 380 | |
0e32b39c DA |
381 | /* TODO - validate mode against available PBN for link */ |
382 | if (mode->clock < 10000) | |
383 | return MODE_CLOCK_LOW; | |
384 | ||
385 | if (mode->flags & DRM_MODE_FLAG_DBLCLK) | |
386 | return MODE_H_ILLEGAL; | |
387 | ||
22a2c8e0 | 388 | if (mode_rate > max_rate || mode->clock > max_dotclk) |
832d5bfd MK |
389 | return MODE_CLOCK_HIGH; |
390 | ||
0e32b39c DA |
391 | return MODE_OK; |
392 | } | |
393 | ||
459485ad DV |
394 | static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector, |
395 | struct drm_connector_state *state) | |
396 | { | |
397 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
398 | struct intel_dp *intel_dp = intel_connector->mst_port; | |
399 | struct intel_crtc *crtc = to_intel_crtc(state->crtc); | |
400 | ||
0552f765 DA |
401 | if (!intel_dp) |
402 | return NULL; | |
459485ad DV |
403 | return &intel_dp->mst_encoders[crtc->pipe]->base.base; |
404 | } | |
405 | ||
0e32b39c DA |
406 | static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector) |
407 | { | |
408 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
409 | struct intel_dp *intel_dp = intel_connector->mst_port; | |
0552f765 DA |
410 | if (!intel_dp) |
411 | return NULL; | |
0e32b39c DA |
412 | return &intel_dp->mst_encoders[0]->base.base; |
413 | } | |
414 | ||
415 | static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = { | |
416 | .get_modes = intel_dp_mst_get_modes, | |
417 | .mode_valid = intel_dp_mst_mode_valid, | |
459485ad | 418 | .atomic_best_encoder = intel_mst_atomic_best_encoder, |
0e32b39c | 419 | .best_encoder = intel_mst_best_encoder, |
f424f55e | 420 | .atomic_check = intel_dp_mst_atomic_check, |
0e32b39c DA |
421 | }; |
422 | ||
423 | static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder) | |
424 | { | |
425 | struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder); | |
426 | ||
427 | drm_encoder_cleanup(encoder); | |
428 | kfree(intel_mst); | |
429 | } | |
430 | ||
431 | static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = { | |
432 | .destroy = intel_dp_mst_encoder_destroy, | |
433 | }; | |
434 | ||
435 | static bool intel_dp_mst_get_hw_state(struct intel_connector *connector) | |
436 | { | |
e85376cb | 437 | if (connector->encoder && connector->base.state->crtc) { |
0e32b39c DA |
438 | enum pipe pipe; |
439 | if (!connector->encoder->get_hw_state(connector->encoder, &pipe)) | |
440 | return false; | |
441 | return true; | |
442 | } | |
443 | return false; | |
444 | } | |
445 | ||
12e6cecd | 446 | static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *pathprop) |
0e32b39c DA |
447 | { |
448 | struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); | |
449 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); | |
450 | struct drm_device *dev = intel_dig_port->base.base.dev; | |
4d58443d | 451 | struct drm_i915_private *dev_priv = to_i915(dev); |
0e32b39c DA |
452 | struct intel_connector *intel_connector; |
453 | struct drm_connector *connector; | |
4d58443d | 454 | enum pipe pipe; |
091a4f91 | 455 | int ret; |
0e32b39c | 456 | |
9bdbd0b9 | 457 | intel_connector = intel_connector_alloc(); |
0e32b39c DA |
458 | if (!intel_connector) |
459 | return NULL; | |
460 | ||
461 | connector = &intel_connector->base; | |
091a4f91 JA |
462 | ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, |
463 | DRM_MODE_CONNECTOR_DisplayPort); | |
464 | if (ret) { | |
465 | intel_connector_free(intel_connector); | |
466 | return NULL; | |
467 | } | |
468 | ||
0e32b39c DA |
469 | drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs); |
470 | ||
0e32b39c DA |
471 | intel_connector->get_hw_state = intel_dp_mst_get_hw_state; |
472 | intel_connector->mst_port = intel_dp; | |
473 | intel_connector->port = port; | |
474 | ||
4d58443d | 475 | for_each_pipe(dev_priv, pipe) { |
091a4f91 JA |
476 | struct drm_encoder *enc = |
477 | &intel_dp->mst_encoders[pipe]->base.base; | |
478 | ||
479 | ret = drm_mode_connector_attach_encoder(&intel_connector->base, | |
480 | enc); | |
481 | if (ret) | |
482 | goto err; | |
0e32b39c | 483 | } |
0e32b39c DA |
484 | |
485 | drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0); | |
6f134d7b DA |
486 | drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0); |
487 | ||
091a4f91 JA |
488 | ret = drm_mode_connector_set_path_property(connector, pathprop); |
489 | if (ret) | |
490 | goto err; | |
491 | ||
d9515c5e | 492 | return connector; |
091a4f91 JA |
493 | |
494 | err: | |
495 | drm_connector_cleanup(connector); | |
496 | return NULL; | |
d9515c5e DA |
497 | } |
498 | ||
499 | static void intel_dp_register_mst_connector(struct drm_connector *connector) | |
500 | { | |
666b7cdc | 501 | struct drm_i915_private *dev_priv = to_i915(connector->dev); |
7a418e34 | 502 | |
666b7cdc DV |
503 | if (dev_priv->fbdev) |
504 | drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, | |
505 | connector); | |
7a418e34 | 506 | |
666b7cdc | 507 | drm_connector_register(connector); |
0e32b39c DA |
508 | } |
509 | ||
510 | static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, | |
511 | struct drm_connector *connector) | |
512 | { | |
513 | struct intel_connector *intel_connector = to_intel_connector(connector); | |
666b7cdc | 514 | struct drm_i915_private *dev_priv = to_i915(connector->dev); |
20fae983 | 515 | |
dd59a9ba | 516 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); |
c191eca1 | 517 | drm_connector_unregister(connector); |
1f771755 | 518 | |
666b7cdc DV |
519 | if (dev_priv->fbdev) |
520 | drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, | |
521 | connector); | |
af2405af TR |
522 | /* prevent race with the check in ->detect */ |
523 | drm_modeset_lock(&connector->dev->mode_config.connection_mutex, NULL); | |
0552f765 | 524 | intel_connector->mst_port = NULL; |
af2405af | 525 | drm_modeset_unlock(&connector->dev->mode_config.connection_mutex); |
0e32b39c | 526 | |
666b7cdc | 527 | drm_connector_unreference(connector); |
0e32b39c DA |
528 | } |
529 | ||
530 | static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) | |
531 | { | |
532 | struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); | |
533 | struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); | |
534 | struct drm_device *dev = intel_dig_port->base.base.dev; | |
535 | ||
536 | drm_kms_helper_hotplug_event(dev); | |
537 | } | |
538 | ||
69a0f89c | 539 | static const struct drm_dp_mst_topology_cbs mst_cbs = { |
0e32b39c | 540 | .add_connector = intel_dp_add_mst_connector, |
d9515c5e | 541 | .register_connector = intel_dp_register_mst_connector, |
0e32b39c DA |
542 | .destroy_connector = intel_dp_destroy_mst_connector, |
543 | .hotplug = intel_dp_mst_hotplug, | |
544 | }; | |
545 | ||
546 | static struct intel_dp_mst_encoder * | |
547 | intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe) | |
548 | { | |
549 | struct intel_dp_mst_encoder *intel_mst; | |
550 | struct intel_encoder *intel_encoder; | |
551 | struct drm_device *dev = intel_dig_port->base.base.dev; | |
552 | ||
553 | intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL); | |
554 | ||
555 | if (!intel_mst) | |
556 | return NULL; | |
557 | ||
558 | intel_mst->pipe = pipe; | |
559 | intel_encoder = &intel_mst->base; | |
560 | intel_mst->primary = intel_dig_port; | |
561 | ||
562 | drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs, | |
580d8ed5 | 563 | DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe)); |
0e32b39c DA |
564 | |
565 | intel_encoder->type = INTEL_OUTPUT_DP_MST; | |
79f255a0 | 566 | intel_encoder->power_domain = intel_dig_port->base.power_domain; |
8f4f2797 | 567 | intel_encoder->port = intel_dig_port->base.port; |
0e32b39c DA |
568 | intel_encoder->crtc_mask = 0x7; |
569 | intel_encoder->cloneable = 0; | |
570 | ||
571 | intel_encoder->compute_config = intel_dp_mst_compute_config; | |
572 | intel_encoder->disable = intel_mst_disable_dp; | |
573 | intel_encoder->post_disable = intel_mst_post_disable_dp; | |
5161d058 | 574 | intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp; |
0e32b39c DA |
575 | intel_encoder->pre_enable = intel_mst_pre_enable_dp; |
576 | intel_encoder->enable = intel_mst_enable_dp; | |
577 | intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state; | |
578 | intel_encoder->get_config = intel_dp_mst_enc_get_config; | |
579 | ||
580 | return intel_mst; | |
581 | ||
582 | } | |
583 | ||
584 | static bool | |
585 | intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port) | |
586 | { | |
0e32b39c | 587 | struct intel_dp *intel_dp = &intel_dig_port->dp; |
4d58443d MK |
588 | struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev); |
589 | enum pipe pipe; | |
0e32b39c | 590 | |
4d58443d MK |
591 | for_each_pipe(dev_priv, pipe) |
592 | intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(intel_dig_port, pipe); | |
0e32b39c DA |
593 | return true; |
594 | } | |
595 | ||
596 | int | |
597 | intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id) | |
598 | { | |
599 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
600 | struct drm_device *dev = intel_dig_port->base.base.dev; | |
601 | int ret; | |
602 | ||
603 | intel_dp->can_mst = true; | |
604 | intel_dp->mst_mgr.cbs = &mst_cbs; | |
605 | ||
606 | /* create encoders */ | |
607 | intel_dp_create_fake_mst_encoders(intel_dig_port); | |
7b0a89a6 DP |
608 | ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev, |
609 | &intel_dp->aux, 16, 3, conn_base_id); | |
0e32b39c DA |
610 | if (ret) { |
611 | intel_dp->can_mst = false; | |
612 | return ret; | |
613 | } | |
614 | return 0; | |
615 | } | |
616 | ||
617 | void | |
618 | intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port) | |
619 | { | |
620 | struct intel_dp *intel_dp = &intel_dig_port->dp; | |
621 | ||
622 | if (!intel_dp->can_mst) | |
623 | return; | |
624 | ||
625 | drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr); | |
626 | /* encoders will get killed by normal cleanup */ | |
627 | } |