2014-05-02 11:02:48 +07:00
|
|
|
/*
|
|
|
|
* Copyright © 2008 Intel Corporation
|
|
|
|
* 2014 Red Hat Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
* Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <drm/drmP.h>
|
|
|
|
#include "i915_drv.h"
|
|
|
|
#include "intel_drv.h"
|
2015-01-23 07:50:32 +07:00
|
|
|
#include <drm/drm_atomic_helper.h>
|
2014-05-02 11:02:48 +07:00
|
|
|
#include <drm/drm_crtc_helper.h>
|
|
|
|
#include <drm/drm_edid.h>
|
|
|
|
|
|
|
|
static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
2016-08-09 22:04:05 +07:00
|
|
|
struct intel_crtc_state *pipe_config,
|
|
|
|
struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
2017-10-24 16:52:14 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2014-05-02 11:02:48 +07:00
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2018-10-09 06:24:33 +07:00
|
|
|
struct drm_connector *connector = conn_state->connector;
|
|
|
|
void *port = to_intel_connector(connector)->port;
|
2017-04-29 06:14:20 +07:00
|
|
|
struct drm_atomic_state *state = pipe_config->base.state;
|
2016-08-09 22:04:06 +07:00
|
|
|
int bpp;
|
2018-10-09 06:24:33 +07:00
|
|
|
int lane_count, slots = 0;
|
2015-09-08 17:40:49 +07:00
|
|
|
const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
|
2014-05-02 11:02:48 +07:00
|
|
|
int mst_pbn;
|
2018-09-12 13:22:50 +07:00
|
|
|
bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
|
|
|
|
DP_DPCD_QUIRK_CONSTANT_N);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-05-24 19:54:03 +07:00
|
|
|
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
|
|
return false;
|
|
|
|
|
2018-10-12 13:23:07 +07:00
|
|
|
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
|
2014-05-02 11:02:48 +07:00
|
|
|
pipe_config->has_pch_encoder = false;
|
|
|
|
bpp = 24;
|
2017-01-24 23:21:49 +07:00
|
|
|
if (intel_dp->compliance.test_data.bpc) {
|
|
|
|
bpp = intel_dp->compliance.test_data.bpc * 3;
|
|
|
|
DRM_DEBUG_KMS("Setting pipe bpp to %d\n",
|
|
|
|
bpp);
|
|
|
|
}
|
2014-05-02 11:02:48 +07:00
|
|
|
/*
|
|
|
|
* for MST we always configure max link bw - the spec doesn't
|
|
|
|
* seem to suggest we should do otherwise.
|
|
|
|
*/
|
2017-04-06 20:44:14 +07:00
|
|
|
lane_count = intel_dp_max_lane_count(intel_dp);
|
2015-03-12 22:10:36 +07:00
|
|
|
|
2015-07-06 20:39:15 +07:00
|
|
|
pipe_config->lane_count = lane_count;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-01-24 23:21:49 +07:00
|
|
|
pipe_config->pipe_bpp = bpp;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-04-29 06:14:20 +07:00
|
|
|
pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
|
2015-03-20 21:18:13 +07:00
|
|
|
|
2018-10-09 06:24:33 +07:00
|
|
|
if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, port))
|
2016-11-28 19:07:07 +07:00
|
|
|
pipe_config->has_audio = true;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-04-29 06:14:20 +07:00
|
|
|
mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
|
2014-05-02 11:02:48 +07:00
|
|
|
pipe_config->pbn = mst_pbn;
|
2017-04-29 06:14:20 +07:00
|
|
|
|
2018-10-09 06:24:33 +07:00
|
|
|
/* Zombie connectors can't have VCPI slots */
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (!drm_connector_is_unregistered(connector)) {
|
2018-10-09 06:24:33 +07:00
|
|
|
slots = drm_dp_atomic_find_vcpi_slots(state,
|
|
|
|
&intel_dp->mst_mgr,
|
|
|
|
port,
|
|
|
|
mst_pbn);
|
|
|
|
if (slots < 0) {
|
|
|
|
DRM_DEBUG_KMS("failed finding vcpi slots:%d\n",
|
|
|
|
slots);
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-29 06:14:20 +07:00
|
|
|
}
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
intel_link_compute_m_n(bpp, lane_count,
|
|
|
|
adjusted_mode->crtc_clock,
|
|
|
|
pipe_config->port_clock,
|
2017-05-18 18:10:25 +07:00
|
|
|
&pipe_config->dp_m_n,
|
2018-09-12 13:22:50 +07:00
|
|
|
constant_n);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
pipe_config->dp_m_n.tu = slots;
|
2015-08-31 15:23:28 +07:00
|
|
|
|
2017-10-27 20:43:48 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv))
|
|
|
|
pipe_config->lane_lat_optim_mask =
|
|
|
|
bxt_ddi_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
|
|
|
|
|
2017-10-24 16:52:14 +07:00
|
|
|
intel_ddi_compute_min_voltage_level(dev_priv, pipe_config);
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
return true;
|
2017-04-29 06:14:20 +07:00
|
|
|
}
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-04-29 06:14:20 +07:00
|
|
|
static int intel_dp_mst_atomic_check(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *new_conn_state)
|
|
|
|
{
|
|
|
|
struct drm_atomic_state *state = new_conn_state->state;
|
|
|
|
struct drm_connector_state *old_conn_state;
|
|
|
|
struct drm_crtc *old_crtc;
|
|
|
|
struct drm_crtc_state *crtc_state;
|
|
|
|
int slots, ret = 0;
|
|
|
|
|
|
|
|
old_conn_state = drm_atomic_get_old_connector_state(state, connector);
|
|
|
|
old_crtc = old_conn_state->crtc;
|
|
|
|
if (!old_crtc)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
|
|
|
|
slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
|
|
|
|
if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
|
|
|
|
struct drm_dp_mst_topology_mgr *mgr;
|
|
|
|
struct drm_encoder *old_encoder;
|
|
|
|
|
|
|
|
old_encoder = old_conn_state->best_encoder;
|
|
|
|
mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
|
|
|
|
|
|
|
|
ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots);
|
|
|
|
if (ret)
|
|
|
|
DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret);
|
|
|
|
else
|
|
|
|
to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
|
|
|
|
}
|
|
|
|
return ret;
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_disable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *old_crtc_state,
|
|
|
|
const struct drm_connector_state *old_conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(old_conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
int ret;
|
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2016-08-09 22:04:12 +07:00
|
|
|
drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("failed to update payload %d\n", ret);
|
|
|
|
}
|
2016-12-15 21:29:43 +07:00
|
|
|
if (old_crtc_state->has_audio)
|
2017-10-31 01:46:53 +07:00
|
|
|
intel_audio_codec_disable(encoder,
|
|
|
|
old_crtc_state, old_conn_state);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *old_crtc_state,
|
|
|
|
const struct drm_connector_state *old_conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(old_conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-09-01 00:47:39 +07:00
|
|
|
intel_ddi_disable_pipe_clock(old_crtc_state);
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
/* this can fail */
|
|
|
|
drm_dp_check_act_status(&intel_dp->mst_mgr);
|
|
|
|
/* and this can also fail */
|
|
|
|
drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
|
|
|
|
2016-08-09 22:04:12 +07:00
|
|
|
drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-10-03 21:22:11 +07:00
|
|
|
/*
|
|
|
|
* Power down mst path before disabling the port, otherwise we end
|
|
|
|
* up getting interrupts from the sink upon detecting link loss.
|
|
|
|
*/
|
|
|
|
drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
|
|
|
|
false);
|
|
|
|
|
2016-08-05 23:05:42 +07:00
|
|
|
intel_dp->active_mst_links--;
|
2016-03-09 08:14:38 +07:00
|
|
|
|
|
|
|
intel_mst->connector = NULL;
|
2018-04-07 08:10:53 +07:00
|
|
|
if (intel_dp->active_mst_links == 0) {
|
|
|
|
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
|
2016-08-09 22:04:04 +07:00
|
|
|
intel_dig_port->base.post_disable(&intel_dig_port->base,
|
2017-10-28 02:31:27 +07:00
|
|
|
old_crtc_state, NULL);
|
2018-04-07 08:10:53 +07:00
|
|
|
}
|
2017-10-28 02:31:27 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2017-10-27 20:43:48 +07:00
|
|
|
static void intel_mst_pre_pll_enable_dp(struct intel_encoder *encoder,
|
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
|
|
|
|
if (intel_dp->active_mst_links == 0 &&
|
|
|
|
intel_dig_port->base.pre_pll_enable)
|
|
|
|
intel_dig_port->base.pre_pll_enable(&intel_dig_port->base,
|
|
|
|
pipe_config, NULL);
|
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2017-11-09 22:24:34 +07:00
|
|
|
enum port port = intel_dig_port->base.port;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
int ret;
|
|
|
|
uint32_t temp;
|
|
|
|
|
2015-08-27 18:13:31 +07:00
|
|
|
/* MST encoders are bound to a crtc, not to a connector,
|
|
|
|
* force the mapping here for get_hw_state.
|
|
|
|
*/
|
2016-08-09 22:04:12 +07:00
|
|
|
connector->encoder = encoder;
|
|
|
|
intel_mst->connector = connector;
|
2015-08-27 18:13:31 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2016-03-09 08:14:38 +07:00
|
|
|
|
2018-04-07 08:10:53 +07:00
|
|
|
if (intel_dp->active_mst_links == 0)
|
|
|
|
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
|
|
|
|
2017-10-03 21:22:11 +07:00
|
|
|
drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
|
2018-04-07 08:10:53 +07:00
|
|
|
|
2017-03-02 19:58:57 +07:00
|
|
|
if (intel_dp->active_mst_links == 0)
|
|
|
|
intel_dig_port->base.pre_enable(&intel_dig_port->base,
|
|
|
|
pipe_config, NULL);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
|
2016-08-09 22:04:12 +07:00
|
|
|
connector->port,
|
2017-03-16 14:10:26 +07:00
|
|
|
pipe_config->pbn,
|
|
|
|
pipe_config->dp_m_n.tu);
|
2018-07-19 00:19:43 +07:00
|
|
|
if (!ret)
|
2014-05-02 11:02:48 +07:00
|
|
|
DRM_ERROR("failed to allocate vcpi\n");
|
|
|
|
|
2016-08-05 23:05:42 +07:00
|
|
|
intel_dp->active_mst_links++;
|
2014-05-02 11:02:48 +07:00
|
|
|
temp = I915_READ(DP_TP_STATUS(port));
|
|
|
|
I915_WRITE(DP_TP_STATUS(port), temp);
|
|
|
|
|
|
|
|
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
2018-09-01 00:47:39 +07:00
|
|
|
|
|
|
|
intel_ddi_enable_pipe_clock(pipe_config);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_enable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2017-11-09 22:24:34 +07:00
|
|
|
enum port port = intel_dig_port->base.port;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2016-06-30 21:33:11 +07:00
|
|
|
if (intel_wait_for_register(dev_priv,
|
|
|
|
DP_TP_STATUS(port),
|
|
|
|
DP_TP_STATUS_ACT_SENT,
|
|
|
|
DP_TP_STATUS_ACT_SENT,
|
|
|
|
1))
|
2014-05-02 11:02:48 +07:00
|
|
|
DRM_ERROR("Timed out waiting for ACT sent\n");
|
|
|
|
|
2018-07-20 06:42:17 +07:00
|
|
|
drm_dp_check_act_status(&intel_dp->mst_mgr);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-07-20 06:42:17 +07:00
|
|
|
drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
2016-12-15 21:29:43 +07:00
|
|
|
if (pipe_config->has_audio)
|
2016-11-28 19:07:07 +07:00
|
|
|
intel_audio_codec_enable(encoder, pipe_config, conn_state);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
|
|
|
|
enum pipe *pipe)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
*pipe = intel_mst->pipe;
|
2016-03-09 08:14:38 +07:00
|
|
|
if (intel_mst->connector)
|
2014-05-02 11:02:48 +07:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
|
2015-01-15 19:55:21 +07:00
|
|
|
struct intel_crtc_state *pipe_config)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
2017-10-24 16:52:14 +07:00
|
|
|
|
2017-10-28 02:31:28 +07:00
|
|
|
intel_ddi_get_config(&intel_dig_port->base, pipe_config);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
struct edid *edid;
|
|
|
|
int ret;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2016-03-09 08:14:38 +07:00
|
|
|
return intel_connector_update_modes(connector, NULL);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2016-03-09 08:14:38 +07:00
|
|
|
edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
ret = intel_connector_update_modes(connector, edid);
|
|
|
|
kfree(edid);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_connector_status
|
2014-10-22 16:06:44 +07:00
|
|
|
intel_dp_mst_detect(struct drm_connector *connector, bool force)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2016-03-09 08:14:38 +07:00
|
|
|
return connector_status_disconnected;
|
2018-10-09 06:24:32 +07:00
|
|
|
return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
|
|
|
|
intel_connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
|
|
|
|
.detect = intel_dp_mst_detect,
|
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
2016-06-24 20:00:15 +07:00
|
|
|
.late_register = intel_connector_register,
|
2016-06-17 17:40:33 +07:00
|
|
|
.early_unregister = intel_connector_unregister,
|
2018-10-09 21:11:03 +07:00
|
|
|
.destroy = intel_connector_destroy,
|
2015-01-23 07:50:32 +07:00
|
|
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
2015-03-20 21:18:06 +07:00
|
|
|
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
2014-05-02 11:02:48 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int intel_dp_mst_get_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
return intel_dp_mst_get_ddc_modes(connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_mode_status
|
|
|
|
intel_dp_mst_mode_valid(struct drm_connector *connector,
|
|
|
|
struct drm_display_mode *mode)
|
|
|
|
{
|
2016-11-16 03:59:06 +07:00
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
2016-02-02 20:16:40 +07:00
|
|
|
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
|
2016-11-16 03:59:06 +07:00
|
|
|
int bpp = 24; /* MST uses fixed bpp */
|
|
|
|
int max_rate, mode_rate, max_lanes, max_link_clock;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2017-08-10 21:50:43 +07:00
|
|
|
return MODE_ERROR;
|
|
|
|
|
2018-05-24 19:54:03 +07:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
|
|
return MODE_NO_DBLESCAN;
|
|
|
|
|
2016-11-16 03:59:06 +07:00
|
|
|
max_link_clock = intel_dp_max_link_rate(intel_dp);
|
2017-04-06 20:44:14 +07:00
|
|
|
max_lanes = intel_dp_max_lane_count(intel_dp);
|
2016-11-16 03:59:06 +07:00
|
|
|
|
|
|
|
max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
|
|
|
|
mode_rate = intel_dp_link_required(mode->clock, bpp);
|
2016-02-02 20:16:40 +07:00
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
/* TODO - validate mode against available PBN for link */
|
|
|
|
if (mode->clock < 10000)
|
|
|
|
return MODE_CLOCK_LOW;
|
|
|
|
|
|
|
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
|
|
|
return MODE_H_ILLEGAL;
|
|
|
|
|
2016-11-16 03:59:06 +07:00
|
|
|
if (mode_rate > max_rate || mode->clock > max_dotclk)
|
2016-02-02 20:16:40 +07:00
|
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
return MODE_OK;
|
|
|
|
}
|
|
|
|
|
2015-08-03 22:24:09 +07:00
|
|
|
static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *state)
|
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
struct intel_crtc *crtc = to_intel_crtc(state->crtc);
|
|
|
|
|
|
|
|
return &intel_dp->mst_encoders[crtc->pipe]->base.base;
|
|
|
|
}
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
|
|
|
|
.get_modes = intel_dp_mst_get_modes,
|
|
|
|
.mode_valid = intel_dp_mst_mode_valid,
|
2015-08-03 22:24:09 +07:00
|
|
|
.atomic_best_encoder = intel_mst_atomic_best_encoder,
|
2017-04-29 06:14:20 +07:00
|
|
|
.atomic_check = intel_dp_mst_atomic_check,
|
2014-05-02 11:02:48 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
|
|
|
|
|
|
|
|
drm_encoder_cleanup(encoder);
|
|
|
|
kfree(intel_mst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
|
|
|
|
.destroy = intel_dp_mst_encoder_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
|
|
|
|
{
|
2015-08-27 18:13:31 +07:00
|
|
|
if (connector->encoder && connector->base.state->crtc) {
|
2014-05-02 11:02:48 +07:00
|
|
|
enum pipe pipe;
|
|
|
|
if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-13 16:38:36 +07:00
|
|
|
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)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
|
|
|
|
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
2017-10-13 17:24:46 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
2014-05-02 11:02:48 +07:00
|
|
|
struct intel_connector *intel_connector;
|
|
|
|
struct drm_connector *connector;
|
2017-10-13 17:24:46 +07:00
|
|
|
enum pipe pipe;
|
2017-10-14 01:01:44 +07:00
|
|
|
int ret;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2015-04-10 14:59:10 +07:00
|
|
|
intel_connector = intel_connector_alloc();
|
2014-05-02 11:02:48 +07:00
|
|
|
if (!intel_connector)
|
|
|
|
return NULL;
|
|
|
|
|
2018-11-07 04:30:12 +07:00
|
|
|
intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
|
|
|
|
intel_connector->mst_port = intel_dp;
|
|
|
|
intel_connector->port = port;
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
connector = &intel_connector->base;
|
2017-10-14 01:01:44 +07:00
|
|
|
ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
|
|
|
|
DRM_MODE_CONNECTOR_DisplayPort);
|
|
|
|
if (ret) {
|
|
|
|
intel_connector_free(intel_connector);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
|
|
|
|
|
2017-10-13 17:24:46 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-10-14 01:01:44 +07:00
|
|
|
struct drm_encoder *enc =
|
|
|
|
&intel_dp->mst_encoders[pipe]->base.base;
|
|
|
|
|
2018-07-09 15:40:07 +07:00
|
|
|
ret = drm_connector_attach_encoder(&intel_connector->base, enc);
|
2017-10-14 01:01:44 +07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
|
2014-10-20 13:30:50 +07:00
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);
|
|
|
|
|
2018-07-09 15:40:08 +07:00
|
|
|
ret = drm_connector_set_path_property(connector, pathprop);
|
2017-10-14 01:01:44 +07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2015-09-16 14:55:23 +07:00
|
|
|
return connector;
|
2017-10-14 01:01:44 +07:00
|
|
|
|
|
|
|
err:
|
|
|
|
drm_connector_cleanup(connector);
|
|
|
|
return NULL;
|
2015-09-16 14:55:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_register_mst_connector(struct drm_connector *connector)
|
|
|
|
{
|
2017-07-04 22:18:22 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
2016-06-24 20:00:14 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
if (dev_priv->fbdev)
|
|
|
|
drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper,
|
|
|
|
connector);
|
2016-06-24 20:00:14 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
drm_connector_register(connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|
|
|
struct drm_connector *connector)
|
|
|
|
{
|
2017-07-04 22:18:22 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
2015-08-06 18:47:36 +07:00
|
|
|
|
2017-09-19 05:21:37 +07:00
|
|
|
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name);
|
2016-06-17 17:40:33 +07:00
|
|
|
drm_connector_unregister(connector);
|
drm/i915: Fix race condition in intel_dp_destroy_mst_connector()
After unplugging a DP MST display from the system, we have to go through
and destroy all of the DRM connectors associated with it since none of
them are valid anymore. Unfortunately, intel_dp_destroy_mst_connector()
doesn't do a good enough job of ensuring that throughout the destruction
process that no modesettings can be done with the connectors. As it is
right now, intel_dp_destroy_mst_connector() works like this:
* Take all modeset locks
* Clear the configuration of the crtc on the connector, if there is one
* Drop all modeset locks, this is required because of circular
dependency issues that arise with trying to remove the connector from
sysfs with modeset locks held
* Unregister the connector
* Take all modeset locks, again
* Do the rest of the required cleaning for destroying the connector
* Finally drop all modeset locks for good
This only works sometimes. During the destruction process, it's very
possible that a userspace application will attempt to do a modesetting
using the connector. When we drop the modeset locks, an ioctl handler
such as drm_mode_setcrtc has the oppurtunity to take all of the modeset
locks from us. When this happens, one thing leads to another and
eventually we end up committing a mode with the non-existent connector:
[drm:intel_dp_link_training_clock_recovery [i915]] *ERROR* failed to enable link training
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_dp_start_link_train [i915]] *ERROR* failed to start channel equalization
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_mst_pre_enable_dp [i915]] *ERROR* failed to allocate vcpi
And in some cases, such as with the T460s using an MST dock, this
results in breaking modesetting and/or panicking the system.
To work around this, we now unregister the connector at the very
beginning of intel_dp_destroy_mst_connector(), grab all the modesetting
locks, and then hold them until we finish the rest of the function.
CC: stable@vger.kernel.org
Signed-off-by: Lyude <cpaul@redhat.com>
Signed-off-by: Rob Clark <rclark@redhat.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1458155884-13877-1-git-send-email-cpaul@redhat.com
2016-03-17 02:18:04 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
if (dev_priv->fbdev)
|
|
|
|
drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper,
|
|
|
|
connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-06-18 18:01:50 +07:00
|
|
|
drm_connector_put(connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
|
|
|
|
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
|
|
|
|
|
drm_kms_helper_hotplug_event(dev);
|
|
|
|
}
|
|
|
|
|
2015-12-31 04:20:30 +07:00
|
|
|
static const struct drm_dp_mst_topology_cbs mst_cbs = {
|
2014-05-02 11:02:48 +07:00
|
|
|
.add_connector = intel_dp_add_mst_connector,
|
2015-09-16 14:55:23 +07:00
|
|
|
.register_connector = intel_dp_register_mst_connector,
|
2014-05-02 11:02:48 +07:00
|
|
|
.destroy_connector = intel_dp_destroy_mst_connector,
|
|
|
|
.hotplug = intel_dp_mst_hotplug,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct intel_dp_mst_encoder *
|
|
|
|
intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst;
|
|
|
|
struct intel_encoder *intel_encoder;
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
|
|
|
|
|
intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!intel_mst)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
intel_mst->pipe = pipe;
|
|
|
|
intel_encoder = &intel_mst->base;
|
|
|
|
intel_mst->primary = intel_dig_port;
|
|
|
|
|
|
|
|
drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
|
2016-05-28 00:59:24 +07:00
|
|
|
DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
intel_encoder->type = INTEL_OUTPUT_DP_MST;
|
2017-02-22 13:34:27 +07:00
|
|
|
intel_encoder->power_domain = intel_dig_port->base.power_domain;
|
2017-11-09 22:24:34 +07:00
|
|
|
intel_encoder->port = intel_dig_port->base.port;
|
2014-05-02 11:02:48 +07:00
|
|
|
intel_encoder->crtc_mask = 0x7;
|
|
|
|
intel_encoder->cloneable = 0;
|
|
|
|
|
|
|
|
intel_encoder->compute_config = intel_dp_mst_compute_config;
|
|
|
|
intel_encoder->disable = intel_mst_disable_dp;
|
|
|
|
intel_encoder->post_disable = intel_mst_post_disable_dp;
|
2017-10-27 20:43:48 +07:00
|
|
|
intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
|
2014-05-02 11:02:48 +07:00
|
|
|
intel_encoder->pre_enable = intel_mst_pre_enable_dp;
|
|
|
|
intel_encoder->enable = intel_mst_enable_dp;
|
|
|
|
intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
|
|
|
|
intel_encoder->get_config = intel_dp_mst_enc_get_config;
|
|
|
|
|
|
|
|
return intel_mst;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2017-10-13 17:24:46 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
|
|
|
|
enum pipe pipe;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-10-13 17:24:46 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
|
|
|
intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(intel_dig_port, pipe);
|
2014-05-02 11:02:48 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
intel_dp->can_mst = true;
|
|
|
|
intel_dp->mst_mgr.cbs = &mst_cbs;
|
|
|
|
|
|
|
|
/* create encoders */
|
|
|
|
intel_dp_create_fake_mst_encoders(intel_dig_port);
|
2017-01-25 06:49:29 +07:00
|
|
|
ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev,
|
|
|
|
&intel_dp->aux, 16, 3, conn_base_id);
|
2014-05-02 11:02:48 +07:00
|
|
|
if (ret) {
|
|
|
|
intel_dp->can_mst = false;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
|
|
|
|
if (!intel_dp->can_mst)
|
|
|
|
return;
|
|
|
|
|
|
|
|
drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
|
|
|
|
/* encoders will get killed by normal cleanup */
|
|
|
|
}
|