summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2020-09-09 05:27:12 +0300
committerDave Airlie <airlied@redhat.com>2020-09-09 05:27:13 +0300
commit877d8c074302c20ac0759d1a4f689c14aada420d (patch)
treec4ac9ed42b665fc88dcc73c957cab62496037ac8 /drivers/gpu
parent1f4b2aca794f7aeb918ed5f0d7221d68a81d6b43 (diff)
parent79416e97dda0118b137302575a70a14259a27d7d (diff)
downloadlinux-877d8c074302c20ac0759d1a4f689c14aada420d.tar.xz
Merge tag 'topic/nouveau-i915-dp-helpers-and-cleanup-2020-08-31-1' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
UAPI Changes: None Cross-subsystem Changes: * Moves a bunch of miscellaneous DP code from the i915 driver into a set of shared DRM DP helpers Core Changes: * New DRM DP helpers (see above) Driver Changes: * Implements usage of the aforementioned DP helpers in the nouveau driver, along with some other various HPD related cleanup for nouveau Signed-off-by: Dave Airlie <airlied@redhat.com> From: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/11e59ebdea7ee4f46803a21fe9b21443d2b9c401.camel@redhat.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c8
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c4
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c187
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c22
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/Kconfig2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c124
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_lspcon.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dac.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c7
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c24
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.h4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv17.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c305
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c132
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c72
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c210
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h48
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm67191.c3
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c31
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c8
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c4
-rw-r--r--drivers/gpu/drm/virtio/Kconfig2
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c55
-rw-r--r--drivers/gpu/drm/vkms/vkms_crtc.c5
30 files changed, 852 insertions, 424 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 371bc099fda2..a18dc878339a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -473,7 +473,7 @@ error:
* help move buffers to and from VRAM.
*/
static int amdgpu_move_blit(struct ttm_buffer_object *bo,
- bool evict, bool no_wait_gpu,
+ bool evict,
struct ttm_resource *new_mem,
struct ttm_resource *old_mem)
{
@@ -571,7 +571,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
}
/* blit VRAM to GTT */
- r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu, &tmp_mem, old_mem);
+ r = amdgpu_move_blit(bo, evict, &tmp_mem, old_mem);
if (unlikely(r)) {
goto out_cleanup;
}
@@ -621,7 +621,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
}
/* copy to VRAM */
- r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu, new_mem, old_mem);
+ r = amdgpu_move_blit(bo, evict, new_mem, old_mem);
if (unlikely(r)) {
goto out_cleanup;
}
@@ -710,7 +710,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
new_mem->mem_type == TTM_PL_VRAM) {
r = amdgpu_move_ram_vram(bo, evict, ctx, new_mem);
} else {
- r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu,
+ r = amdgpu_move_blit(bo, evict,
new_mem, old_mem);
}
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index 1009fc4ed4ed..d734d9402c35 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -960,13 +960,13 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
static int lt9611_parse_dt(struct device *dev,
struct lt9611 *lt9611)
{
- lt9611->dsi0_node = of_graph_get_remote_node(dev->of_node, 1, -1);
+ lt9611->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
if (!lt9611->dsi0_node) {
dev_err(lt9611->dev, "failed to get remote node for primary dsi\n");
return -ENODEV;
}
- lt9611->dsi1_node = of_graph_get_remote_node(dev->of_node, 2, -1);
+ lt9611->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
lt9611->ac_mode = of_property_read_bool(dev->of_node, "lt,ac-mode");
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index ad7c03dac467..1e7c638873c8 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -423,6 +423,133 @@ bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
}
EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
+static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK;
+
+ if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4)
+ port_count = 4;
+
+ return port_count;
+}
+
+static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
+ u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ u8 dpcd_ext[6];
+ int ret;
+
+ /*
+ * Prior to DP1.3 the bit represented by
+ * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
+ * If it is set DP_DPCD_REV at 0000h could be at a value less than
+ * the true capability of the panel. The only way to check is to
+ * then compare 0000h and 2200h.
+ */
+ if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+ DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
+ return 0;
+
+ ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
+ sizeof(dpcd_ext));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(dpcd_ext))
+ return -EIO;
+
+ if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
+ DRM_DEBUG_KMS("%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
+ aux->name, dpcd[DP_DPCD_REV],
+ dpcd_ext[DP_DPCD_REV]);
+ return 0;
+ }
+
+ if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
+ return 0;
+
+ DRM_DEBUG_KMS("%s: Base DPCD: %*ph\n",
+ aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
+
+ memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));
+
+ return 0;
+}
+
+/**
+ * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if
+ * available
+ * @aux: DisplayPort AUX channel
+ * @dpcd: Buffer to store the resulting DPCD in
+ *
+ * Attempts to read the base DPCD caps for @aux. Additionally, this function
+ * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if
+ * present.
+ *
+ * Returns: %0 if the DPCD was read successfully, negative error code
+ * otherwise.
+ */
+int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
+ u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ int ret;
+
+ ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
+ if (ret < 0)
+ return ret;
+ if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
+ return -EIO;
+
+ ret = drm_dp_read_extended_dpcd_caps(aux, dpcd);
+ if (ret < 0)
+ return ret;
+
+ DRM_DEBUG_KMS("%s: DPCD: %*ph\n",
+ aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_dp_read_dpcd_caps);
+
+/**
+ * drm_dp_read_downstream_info() - read DPCD downstream port info if available
+ * @aux: DisplayPort AUX channel
+ * @dpcd: A cached copy of the port's DPCD
+ * @downstream_ports: buffer to store the downstream port info in
+ *
+ * See also:
+ * drm_dp_downstream_max_clock()
+ * drm_dp_downstream_max_bpc()
+ *
+ * Returns: 0 if either the downstream port info was read successfully or
+ * there was no downstream info to read, or a negative error code otherwise.
+ */
+int drm_dp_read_downstream_info(struct drm_dp_aux *aux,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS])
+{
+ int ret;
+ u8 len;
+
+ memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS);
+
+ /* No downstream info to read */
+ if (!drm_dp_is_branch(dpcd) ||
+ dpcd[DP_DPCD_REV] < DP_DPCD_REV_10 ||
+ !(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT))
+ return 0;
+
+ len = drm_dp_downstream_port_count(dpcd);
+ if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE)
+ len *= 4;
+
+ ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len);
+ if (ret < 0)
+ return ret;
+
+ return ret == len ? 0 : -EIO;
+}
+EXPORT_SYMBOL(drm_dp_read_downstream_info);
+
/**
* drm_dp_downstream_max_clock() - extract branch device max
* pixel rate for legacy VGA
@@ -431,7 +558,11 @@ EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
*
- * Returns max clock in kHz on success or 0 if max clock not defined
+ * See also:
+ * drm_dp_read_downstream_info()
+ * drm_dp_downstream_max_bpc()
+ *
+ * Returns: Max clock in kHz on success or 0 if max clock not defined
*/
int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
const u8 port_cap[4])
@@ -462,7 +593,11 @@ EXPORT_SYMBOL(drm_dp_downstream_max_clock);
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
*
- * Returns max bpc on success or 0 if max bpc not defined
+ * See also:
+ * drm_dp_read_downstream_info()
+ * drm_dp_downstream_max_clock()
+ *
+ * Returns: Max bpc on success or 0 if max bpc not defined
*/
int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
const u8 port_cap[4])
@@ -668,6 +803,54 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_dp_set_subconnector_property);
+/**
+ * drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink
+ * count
+ * @connector: The DRM connector to check
+ * @dpcd: A cached copy of the connector's DPCD RX capabilities
+ * @desc: A cached copy of the connector's DP descriptor
+ *
+ * See also: drm_dp_read_sink_count()
+ *
+ * Returns: %True if the (e)DP connector has a valid sink count that should
+ * be probed, %false otherwise.
+ */
+bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const struct drm_dp_desc *desc)
+{
+ /* Some eDP panels don't set a valid value for the sink count */
+ return connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
+ dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 &&
+ dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT &&
+ !drm_dp_has_quirk(desc, 0, DP_DPCD_QUIRK_NO_SINK_COUNT);
+}
+EXPORT_SYMBOL(drm_dp_read_sink_count_cap);
+
+/**
+ * drm_dp_read_sink_count() - Retrieve the sink count for a given sink
+ * @aux: The DP AUX channel to use
+ *
+ * See also: drm_dp_read_sink_count_cap()
+ *
+ * Returns: The current sink count reported by @aux, or a negative error code
+ * otherwise.
+ */
+int drm_dp_read_sink_count(struct drm_dp_aux *aux)
+{
+ u8 count;
+ int ret;
+
+ ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+ return DP_GET_SINK_COUNT(count);
+}
+EXPORT_SYMBOL(drm_dp_read_sink_count);
+
/*
* I2C-over-AUX implementation
*/
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 67dd72ea200e..17dbed0a9800 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3487,6 +3487,28 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count)
}
/**
+ * drm_dp_read_mst_cap() - check whether or not a sink supports MST
+ * @aux: The DP AUX channel to use
+ * @dpcd: A cached copy of the DPCD capabilities for this sink
+ *
+ * Returns: %True if the sink supports MST, %false otherwise
+ */
+bool drm_dp_read_mst_cap(struct drm_dp_aux *aux,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ u8 mstm_cap;
+
+ if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_12)
+ return false;
+
+ if (drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &mstm_cap) != 1)
+ return false;
+
+ return mstm_cap & DP_MST_CAP;
+}
+EXPORT_SYMBOL(drm_dp_read_mst_cap);
+
+/**
* drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
* @mgr: manager to set state for
* @mst_state: true to enable MST on this connector - false to disable.
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
index dfc5aef62f7b..43943e980203 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_HISI_HIBMC
tristate "DRM Support for Hisilicon Hibmc"
- depends on DRM && PCI && MMU && ARM64
+ depends on DRM && PCI && ARM64
select DRM_KMS_HELPER
select DRM_VRAM_HELPER
select DRM_TTM
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 79c27f91f42c..284b15f84592 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -4449,62 +4449,6 @@ intel_dp_link_down(struct intel_encoder *encoder,
}
}
-static void
-intel_dp_extended_receiver_capabilities(struct intel_dp *intel_dp)
-{
- struct drm_i915_private *i915 = dp_to_i915(intel_dp);
- u8 dpcd_ext[6];
-
- /*
- * Prior to DP1.3 the bit represented by
- * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
- * if it is set DP_DPCD_REV at 0000h could be at a value less than
- * the true capability of the panel. The only way to check is to
- * then compare 0000h and 2200h.
- */
- if (!(intel_dp->dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
- DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
- return;
-
- if (drm_dp_dpcd_read(&intel_dp->aux, DP_DP13_DPCD_REV,
- &dpcd_ext, sizeof(dpcd_ext)) != sizeof(dpcd_ext)) {
- drm_err(&i915->drm,
- "DPCD failed read at extended capabilities\n");
- return;
- }
-
- if (intel_dp->dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
- drm_dbg_kms(&i915->drm,
- "DPCD extended DPCD rev less than base DPCD rev\n");
- return;
- }
-
- if (!memcmp(intel_dp->dpcd, dpcd_ext, sizeof(dpcd_ext)))
- return;
-
- drm_dbg_kms(&i915->drm, "Base DPCD: %*ph\n",
- (int)sizeof(intel_dp->dpcd), intel_dp->dpcd);
-
- memcpy(intel_dp->dpcd, dpcd_ext, sizeof(dpcd_ext));
-}
-
-bool
-intel_dp_read_dpcd(struct intel_dp *intel_dp)
-{
- struct drm_i915_private *i915 = dp_to_i915(intel_dp);
-
- if (drm_dp_dpcd_read(&intel_dp->aux, 0x000, intel_dp->dpcd,
- sizeof(intel_dp->dpcd)) < 0)
- return false; /* aux transfer failed */
-
- intel_dp_extended_receiver_capabilities(intel_dp);
-
- drm_dbg_kms(&i915->drm, "DPCD: %*ph\n", (int)sizeof(intel_dp->dpcd),
- intel_dp->dpcd);
-
- return intel_dp->dpcd[DP_DPCD_REV] != 0;
-}
-
bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp)
{
u8 dprx = 0;
@@ -4563,7 +4507,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
/* this function is meant to be called only once */
drm_WARN_ON(&dev_priv->drm, intel_dp->dpcd[DP_DPCD_REV] != 0);
- if (!intel_dp_read_dpcd(intel_dp))
+ if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd) != 0)
return false;
drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc,
@@ -4634,11 +4578,23 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
return true;
}
+static bool
+intel_dp_has_sink_count(struct intel_dp *intel_dp)
+{
+ if (!intel_dp->attached_connector)
+ return false;
+
+ return drm_dp_read_sink_count_cap(&intel_dp->attached_connector->base,
+ intel_dp->dpcd,
+ &intel_dp->desc);
+}
static bool
intel_dp_get_dpcd(struct intel_dp *intel_dp)
{
- if (!intel_dp_read_dpcd(intel_dp))
+ int ret;
+
+ if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd))
return false;
/*
@@ -4653,18 +4609,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
intel_dp_set_common_rates(intel_dp);
}
- /*
- * Some eDP panels do not set a valid value for sink count, that is why
- * it don't care about read it here and in intel_edp_init_dpcd().
- */
- if (!intel_dp_is_edp(intel_dp) &&
- !drm_dp_has_quirk(&intel_dp->desc, 0,
- DP_DPCD_QUIRK_NO_SINK_COUNT)) {
- u8 count;
- ssize_t r;
-
- r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count);
- if (r < 1)
+ if (intel_dp_has_sink_count(intel_dp)) {
+ ret = drm_dp_read_sink_count(&intel_dp->aux);
+ if (ret < 0)
return false;
/*
@@ -4672,7 +4619,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
* a member variable in intel_dp will track any changes
* between short pulse interrupts.
*/
- intel_dp->sink_count = DP_GET_SINK_COUNT(count);
+ intel_dp->sink_count = ret;
/*
* SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
@@ -4685,32 +4632,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
return false;
}
- if (!drm_dp_is_branch(intel_dp->dpcd))
- return true; /* native DP sink */
-
- if (intel_dp->dpcd[DP_DPCD_REV] == 0x10)
- return true; /* no per-port downstream info */
-
- if (drm_dp_dpcd_read(&intel_dp->aux, DP_DOWNSTREAM_PORT_0,
- intel_dp->downstream_ports,
- DP_MAX_DOWNSTREAM_PORTS) < 0)
- return false; /* downstream port status fetch failed */
-
- return true;
-}
-
-static bool
-intel_dp_sink_can_mst(struct intel_dp *intel_dp)
-{
- u8 mstm_cap;
-
- if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
- return false;
-
- if (drm_dp_dpcd_readb(&intel_dp->aux, DP_MSTM_CAP, &mstm_cap) != 1)
- return false;
-
- return mstm_cap & DP_MST_CAP;
+ return drm_dp_read_downstream_info(&intel_dp->aux, intel_dp->dpcd,
+ intel_dp->downstream_ports) == 0;
}
static bool
@@ -4720,7 +4643,7 @@ intel_dp_can_mst(struct intel_dp *intel_dp)
return i915->params.enable_dp_mst &&
intel_dp->can_mst &&
- intel_dp_sink_can_mst(intel_dp);
+ drm_dp_read_mst_cap(&intel_dp->aux, intel_dp->dpcd);
}
static void
@@ -4729,7 +4652,7 @@ intel_dp_configure_mst(struct intel_dp *intel_dp)
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
struct intel_encoder *encoder =
&dp_to_dig_port(intel_dp)->base;
- bool sink_can_mst = intel_dp_sink_can_mst(intel_dp);
+ bool sink_can_mst = drm_dp_read_mst_cap(&intel_dp->aux, intel_dp->dpcd);
drm_dbg_kms(&i915->drm,
"[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n",
@@ -5963,9 +5886,8 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
return connector_status_connected;
/* If we're HPD-aware, SINK_COUNT changes dynamically */
- if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+ if (intel_dp_has_sink_count(intel_dp) &&
intel_dp->downstream_ports[0] & DP_DS_PORT_HPD) {
-
return intel_dp->sink_count ?
connector_status_connected : connector_status_disconnected;
}
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
index b901ab850cbd..0a3af3410d52 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -99,7 +99,6 @@ bool intel_dp_source_supports_hbr3(struct intel_dp *intel_dp);
bool
intel_dp_get_link_status(struct intel_dp *intel_dp, u8 *link_status);
-bool intel_dp_read_dpcd(struct intel_dp *intel_dp);
bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp);
int intel_dp_link_required(int pixel_clock, int bpp);
int intel_dp_max_data_rate(int max_link_clock, int max_lanes);
diff --git a/drivers/gpu/drm/i915/display/intel_lspcon.c b/drivers/gpu/drm/i915/display/intel_lspcon.c
index b781bf469644..dc1b35559afd 100644
--- a/drivers/gpu/drm/i915/display/intel_lspcon.c
+++ b/drivers/gpu/drm/i915/display/intel_lspcon.c
@@ -571,7 +571,7 @@ bool lspcon_init(struct intel_digital_port *dig_port)
return false;
}
- if (!intel_dp_read_dpcd(dp)) {
+ if (drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd) != 0) {
DRM_ERROR("LSPCON DPCD read failed\n");
return false;
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c
index ffdd447d8706..22d10f328559 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dac.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c
@@ -419,7 +419,7 @@ static void nv04_dac_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- nouveau_encoder_connector_get(nv_encoder)->base.name,
+ nv04_encoder_get_connector(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index f9f4482c79b5..42687ea2a4ca 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -184,7 +184,8 @@ static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ struct nouveau_connector *nv_connector =
+ nv04_encoder_get_connector(nv_encoder);
if (!nv_connector->native_mode ||
nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
@@ -478,7 +479,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- nouveau_encoder_connector_get(nv_encoder)->base.name,
+ nv04_encoder_get_connector(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
@@ -591,7 +592,7 @@ static void nv04_dfp_restore(struct drm_encoder *encoder)
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) {
struct nouveau_connector *connector =
- nouveau_encoder_connector_get(nv_encoder);
+ nv04_encoder_get_connector(nv_encoder);
if (connector && connector->native_mode)
call_lvds_script(dev, nv_encoder->dcb, head,
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c
index 900ab69df7e8..3ee836dc5058 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c
@@ -35,9 +35,28 @@
#include <nvif/if0004.h>
+struct nouveau_connector *
+nv04_encoder_get_connector(struct nouveau_encoder *encoder)
+{
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct nouveau_connector *nv_connector = NULL;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->encoder == to_drm_encoder(encoder))
+ nv_connector = nouveau_connector(connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return nv_connector;
+}
+
static void
-nv04_display_fini(struct drm_device *dev, bool suspend)
+nv04_display_fini(struct drm_device *dev, bool runtime, bool suspend)
{
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct nv04_display *disp = nv04_display(dev);
struct drm_crtc *crtc;
@@ -49,6 +68,9 @@ nv04_display_fini(struct drm_device *dev, bool suspend)
if (nv_two_heads(dev))
NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0);
+ if (!runtime)
+ cancel_work_sync(&drm->hpd_work);
+
if (!suspend)
return;
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h
index 495d3284e876..5ace5e906949 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h
@@ -6,6 +6,8 @@
#include "nouveau_display.h"
+struct nouveau_encoder;
+
enum nv04_fp_display_regs {
FP_DISPLAY_END,
FP_TOTAL,
@@ -93,6 +95,8 @@ nv04_display(struct drm_device *dev)
/* nv04_display.c */
int nv04_display_create(struct drm_device *);
+struct nouveau_connector *
+nv04_encoder_get_connector(struct nouveau_encoder *nv_encoder);
/* nv04_crtc.c */
int nv04_crtc_create(struct drm_device *, int index);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
index b701a4d8fe76..3ba7b59580d5 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
@@ -172,7 +172,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- nouveau_encoder_connector_get(nv_encoder)->base.name,
+ nv04_encoder_get_connector(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index 3a9489ed6544..be28e7bd7490 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
@@ -599,7 +599,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n",
- nouveau_encoder_connector_get(nv_encoder)->base.name,
+ nv04_encoder_get_connector(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 1ed242070001..7799530e07c1 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -417,6 +417,40 @@ nv50_outp_atomic_check(struct drm_encoder *encoder,
return 0;
}
+struct nouveau_connector *
+nv50_outp_get_new_connector(struct nouveau_encoder *outp,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector *connector;
+ struct drm_connector_state *connector_state;
+ struct drm_encoder *encoder = to_drm_encoder(outp);
+ int i;
+
+ for_each_new_connector_in_state(state, connector, connector_state, i) {
+ if (connector_state->best_encoder == encoder)
+ return nouveau_connector(connector);
+ }
+
+ return NULL;
+}
+
+struct nouveau_connector *
+nv50_outp_get_old_connector(struct nouveau_encoder *outp,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector *connector;
+ struct drm_connector_state *connector_state;
+ struct drm_encoder *encoder = to_drm_encoder(outp);
+ int i;
+
+ for_each_old_connector_in_state(state, connector, connector_state, i) {
+ if (connector_state->best_encoder == encoder)
+ return nouveau_connector(connector);
+ }
+
+ return NULL;
+}
+
/******************************************************************************
* DAC
*****************************************************************************/
@@ -558,16 +592,31 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
struct nouveau_drm *drm = nouveau_drm(drm_dev);
struct drm_encoder *encoder;
struct nouveau_encoder *nv_encoder;
- struct nouveau_connector *nv_connector;
+ struct drm_connector *connector;
struct nouveau_crtc *nv_crtc;
+ struct drm_connector_list_iter conn_iter;
int ret = 0;
*enabled = false;
+
drm_for_each_encoder(encoder, drm->dev) {
+ struct nouveau_connector *nv_connector = NULL;
+
nv_encoder = nouveau_encoder(encoder);
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
+
+ drm_connector_list_iter_begin(drm_dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->state->best_encoder == encoder) {
+ nv_connector = nouveau_connector(connector);
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+ if (!nv_connector)
+ continue;
+
nv_crtc = nouveau_crtc(encoder->crtc);
- if (!nv_connector || !nv_crtc || nv_encoder->or != port ||
+ if (!nv_crtc || nv_encoder->or != port ||
nv_crtc->index != dev_id)
continue;
*enabled = nv_encoder->audio;
@@ -578,6 +627,7 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
}
break;
}
+
return ret;
}
@@ -671,7 +721,8 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
}
static void
-nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
+nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
+ struct drm_display_mode *mode)
{
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -692,7 +743,7 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
(0x0100 << nv_crtc->index),
};
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
if (!drm_detect_monitor_audio(nv_connector->edid))
return;
@@ -729,7 +780,8 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
}
static void
-nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
+nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
+ struct drm_display_mode *mode)
{
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -758,7 +810,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
int ret;
int size;
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
if (!drm_detect_hdmi_monitor(nv_connector->edid))
return;
@@ -804,7 +856,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
+ args.pwr.vendor_infoframe_length;
nvif_mthd(&disp->disp->object, 0, &args, size);
- nv50_audio_enable(encoder, mode);
+ nv50_audio_enable(encoder, state, mode);
/* If SCDC is supported by the downstream monitor, update
* divider / scrambling settings to what we programmed above.
@@ -833,16 +885,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
#define nv50_mstc(p) container_of((p), struct nv50_mstc, connector)
#define nv50_msto(p) container_of((p), struct nv50_msto, encoder)
-struct nv50_mstm {
- struct nouveau_encoder *outp;
-
- struct drm_dp_mst_topology_mgr mgr;
-
- bool modified;
- bool disabled;
- int links;
-};
-
struct nv50_mstc {
struct nv50_mstm *mstm;
struct drm_dp_mst_port *port;
@@ -1222,7 +1264,10 @@ nv50_mstc_detect(struct drm_connector *connector,
ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr,
mstc->port);
+ if (ret != connector_status_connected)
+ goto out;
+out:
pm_runtime_mark_last_busy(connector->dev->dev);
pm_runtime_put_autosuspend(connector->dev->dev);
return ret;
@@ -1371,41 +1416,51 @@ nv50_mstm = {
.add_connector = nv50_mstm_add_connector,
};
-void
-nv50_mstm_service(struct nv50_mstm *mstm)
+bool
+nv50_mstm_service(struct nouveau_drm *drm,
+ struct nouveau_connector *nv_connector,
+ struct nv50_mstm *mstm)
{
- struct drm_dp_aux *aux = mstm ? mstm->mgr.aux : NULL;
- bool handled = true;
- int ret;
+ struct drm_dp_aux *aux = &nv_connector->aux;
+ bool handled = true, ret = true;
+ int rc;
u8 esi[8] = {};
- if (!aux)
- return;
-
while (handled) {
- ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8);
- if (ret != 8) {
- drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
- return;
+ rc = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8);
+ if (rc != 8) {
+ ret = false;
+ break;
}
drm_dp_mst_hpd_irq(&mstm->mgr, esi, &handled);
if (!handled)
break;
- drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], 3);
+ rc = drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1],
+ 3);
+ if (rc != 3) {
+ ret = false;
+ break;
+ }
}
+
+ if (!ret)
+ NV_DEBUG(drm, "Failed to handle ESI on %s: %d\n",
+ nv_connector->base.name, rc);
+
+ return ret;
}
void
nv50_mstm_remove(struct nv50_mstm *mstm)
{
- if (mstm)
- drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
+ mstm->is_mst = false;
+ drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
}
static int
-nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
+nv50_mstm_enable(struct nv50_mstm *mstm, int state)
{
struct nouveau_encoder *outp = mstm->outp;
struct {
@@ -1420,106 +1475,85 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
};
struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
struct nvif_object *disp = &drm->display->disp.object;
- int ret;
-
- if (dpcd >= 0x12) {
- /* Even if we're enabling MST, start with disabling the
- * branching unit to clear any sink-side MST topology state
- * that wasn't set by us
- */
- ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, 0);
- if (ret < 0)
- return ret;
-
- if (state) {
- /* Now, start initializing */
- ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL,
- DP_MST_EN);
- if (ret < 0)
- return ret;
- }
- }
return nvif_mthd(disp, 0, &args, sizeof(args));
}
int
-nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow)
+nv50_mstm_detect(struct nouveau_encoder *outp)
{
+ struct nv50_mstm *mstm = outp->dp.mstm;
struct drm_dp_aux *aux;
int ret;
- bool old_state, new_state;
- u8 mstm_ctrl;
- if (!mstm)
+ if (!mstm || !mstm->can_mst)
return 0;
- mutex_lock(&mstm->mgr.lock);
-
- old_state = mstm->mgr.mst_state;
- new_state = old_state;
aux = mstm->mgr.aux;
- if (old_state) {
- /* Just check that the MST hub is still as we expect it */
- ret = drm_dp_dpcd_readb(aux, DP_MSTM_CTRL, &mstm_ctrl);
- if (ret < 0 || !(mstm_ctrl & DP_MST_EN)) {
- DRM_DEBUG_KMS("Hub gone, disabling MST topology\n");
- new_state = false;
- }
- } else if (dpcd[0] >= 0x12) {
- ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &dpcd[1]);
- if (ret < 0)
- goto probe_error;
-
- if (!(dpcd[1] & DP_MST_CAP))
- dpcd[0] = 0x11;
- else
- new_state = allow;
- }
-
- if (new_state == old_state) {
- mutex_unlock(&mstm->mgr.lock);
- return new_state;
- }
-
- ret = nv50_mstm_enable(mstm, dpcd[0], new_state);
- if (ret)
- goto probe_error;
-
- mutex_unlock(&mstm->mgr.lock);
+ /* Clear any leftover MST state we didn't set ourselves by first
+ * disabling MST if it was already enabled
+ */
+ ret = drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0);
+ if (ret < 0)
+ return ret;
- ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, new_state);
+ /* And start enabling */
+ ret = nv50_mstm_enable(mstm, true);
if (ret)
- return nv50_mstm_enable(mstm, dpcd[0], 0);
+ return ret;
- return new_state;
+ ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true);
+ if (ret) {
+ nv50_mstm_enable(mstm, false);
+ return ret;
+ }
-probe_error:
- mutex_unlock(&mstm->mgr.lock);
- return ret;
+ mstm->is_mst = true;
+ return 1;
}
static void
-nv50_mstm_fini(struct nv50_mstm *mstm)
+nv50_mstm_fini(struct nouveau_encoder *outp)
{
- if (mstm && mstm->mgr.mst_state)
+ struct nv50_mstm *mstm = outp->dp.mstm;
+
+ if (!mstm)
+ return;
+
+ /* Don't change the MST state of this connector until we've finished
+ * resuming, since we can't safely grab hpd_irq_lock in our resume
+ * path to protect mstm->is_mst without potentially deadlocking
+ */
+ mutex_lock(&outp->dp.hpd_irq_lock);
+ mstm->suspended = true;
+ mutex_unlock(&outp->dp.hpd_irq_lock);
+
+ if (mstm->is_mst)
drm_dp_mst_topology_mgr_suspend(&mstm->mgr);
}
static void
-nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
+nv50_mstm_init(struct nouveau_encoder *outp, bool runtime)
{
- int ret;
+ struct nv50_mstm *mstm = outp->dp.mstm;
+ int ret = 0;
- if (!mstm || !mstm->mgr.mst_state)
+ if (!mstm)
return;
- ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
- if (ret == -1) {
- drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
- drm_kms_helper_hotplug_event(mstm->mgr.dev);
+ if (mstm->is_mst) {
+ ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
+ if (ret == -1)
+ nv50_mstm_remove(mstm);
}
+
+ mutex_lock(&outp->dp.hpd_irq_lock);
+ mstm->suspended = false;
+ mutex_unlock(&outp->dp.hpd_irq_lock);
+
+ if (ret == -1)
+ drm_kms_helper_hotplug_event(mstm->mgr.dev);
}
static void
@@ -1541,17 +1575,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
struct drm_device *dev = outp->base.base.dev;
struct nv50_mstm *mstm;
int ret;
- u8 dpcd;
-
- /* This is a workaround for some monitors not functioning
- * correctly in MST mode on initial module load. I think
- * some bad interaction with the VBIOS may be responsible.
- *
- * A good ol' off and on again seems to work here ;)
- */
- ret = drm_dp_dpcd_readb(aux, DP_DPCD_REV, &dpcd);
- if (ret >= 0 && dpcd >= 0x12)
- drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0);
if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL)))
return -ENOMEM;
@@ -1590,23 +1613,27 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head,
}
static void
-nv50_sor_disable(struct drm_encoder *encoder)
+nv50_sor_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
+ struct nouveau_connector *nv_connector =
+ nv50_outp_get_old_connector(nv_encoder, state);
nv_encoder->crtc = NULL;
if (nv_crtc) {
- struct nvkm_i2c_aux *aux = nv_encoder->aux;
+ struct drm_dp_aux *aux = &nv_connector->aux;
u8 pwr;
- if (aux) {
- int ret = nvkm_rdaux(aux, DP_SET_POWER, &pwr, 1);
+ if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+ int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr);
+
if (ret == 0) {
pwr &= ~DP_SET_POWER_MASK;
pwr |= DP_SET_POWER_D3;
- nvkm_wraux(aux, DP_SET_POWER, &pwr, 1);
+ drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr);
}
}
@@ -1618,7 +1645,8 @@ nv50_sor_disable(struct drm_encoder *encoder)
}
static void
-nv50_sor_enable(struct drm_encoder *encoder)
+nv50_sor_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
@@ -1642,7 +1670,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM;
u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT;
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
nv_encoder->crtc = encoder->crtc;
if ((disp->disp->object.oclass == GT214_DISP ||
@@ -1669,7 +1697,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
}
- nv50_hdmi_enable(&nv_encoder->base.base, mode);
+ nv50_hdmi_enable(&nv_encoder->base.base, state, mode);
break;
case DCB_OUTPUT_LVDS:
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
@@ -1710,7 +1738,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
else
proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B;
- nv50_audio_enable(encoder, mode);
+ nv50_audio_enable(encoder, state, mode);
break;
default:
BUG();
@@ -1723,8 +1751,8 @@ nv50_sor_enable(struct drm_encoder *encoder)
static const struct drm_encoder_helper_funcs
nv50_sor_help = {
.atomic_check = nv50_outp_atomic_check,
- .enable = nv50_sor_enable,
- .disable = nv50_sor_disable,
+ .atomic_enable = nv50_sor_enable,
+ .atomic_disable = nv50_sor_disable,
};
static void
@@ -1733,6 +1761,10 @@ nv50_sor_destroy(struct drm_encoder *encoder)
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
nv50_mstm_del(&nv_encoder->dp.mstm);
drm_encoder_cleanup(encoder);
+
+ if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
+ mutex_destroy(&nv_encoder->dp.hpd_irq_lock);
+
kfree(encoder);
}
@@ -1792,6 +1824,8 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
struct nvkm_i2c_aux *aux =
nvkm_i2c_aux_find(i2c, dcbe->i2c_index);
+ mutex_init(&nv_encoder->dp.hpd_irq_lock);
+
if (aux) {
if (disp->disp->object.oclass < GF110_DISP) {
/* HW has no support for address-only
@@ -2083,7 +2117,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
outp->clr.mask, outp->set.mask);
if (outp->clr.mask) {
- help->disable(encoder);
+ help->atomic_disable(encoder, state);
interlock[NV50_DISP_INTERLOCK_CORE] |= 1;
if (outp->flush_disable) {
nv50_disp_atomic_commit_wndw(state, interlock);
@@ -2122,7 +2156,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
outp->set.mask, outp->clr.mask);
if (outp->set.mask) {
- help->enable(encoder);
+ help->atomic_enable(encoder, state);
interlock[NV50_DISP_INTERLOCK_CORE] = 1;
}
@@ -2490,9 +2524,9 @@ nv50_disp_func = {
*****************************************************************************/
static void
-nv50_display_fini(struct drm_device *dev, bool suspend)
+nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend)
{
- struct nouveau_encoder *nv_encoder;
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_encoder *encoder;
struct drm_plane *plane;
@@ -2504,11 +2538,12 @@ nv50_display_fini(struct drm_device *dev, bool suspend)
}
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
- nv_encoder = nouveau_encoder(encoder);
- nv50_mstm_fini(nv_encoder->dp.mstm);
- }
+ if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST)
+ nv50_mstm_fini(nouveau_encoder(encoder));
}
+
+ if (!runtime)
+ cancel_work_sync(&drm->hpd_work);
}
static int
@@ -2525,7 +2560,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
struct nouveau_encoder *nv_encoder =
nouveau_encoder(encoder);
- nv50_mstm_init(nv_encoder->dp.mstm, runtime);
+ nv50_mstm_init(nv_encoder, runtime);
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 7674025a4bfe..49dd0cbc332f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -391,20 +391,6 @@ find_encoder(struct drm_connector *connector, int type)
return NULL;
}
-struct nouveau_connector *
-nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
-{
- struct drm_device *dev = to_drm_encoder(encoder)->dev;
- struct drm_connector *drm_connector;
-
- list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
- if (drm_connector->encoder == to_drm_encoder(encoder))
- return nouveau_connector(drm_connector);
- }
-
- return NULL;
-}
-
static void
nouveau_connector_destroy(struct drm_connector *connector)
{
@@ -435,7 +421,8 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_DP:
- ret = nouveau_dp_detect(nv_encoder);
+ ret = nouveau_dp_detect(nouveau_connector(connector),
+ nv_encoder);
if (ret == NOUVEAU_DP_MST)
return NULL;
else if (ret == NOUVEAU_DP_SST)
@@ -541,6 +528,17 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
}
}
+static void
+nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
+ struct edid *edid)
+{
+ struct edid *old_edid = nv_connector->edid;
+
+ drm_connector_update_edid_property(&nv_connector->base, edid);
+ kfree(old_edid);
+ nv_connector->edid = edid;
+}
+
static enum drm_connector_status
nouveau_connector_detect(struct drm_connector *connector, bool force)
{
@@ -554,13 +552,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
int ret;
enum drm_connector_status conn_status = connector_status_disconnected;
- /* Cleanup the previous EDID block. */
- if (nv_connector->edid) {
- drm_connector_update_edid_property(connector, NULL);
- kfree(nv_connector->edid);
- nv_connector->edid = NULL;
- }
-
/* Outputs are only polled while runtime active, so resuming the
* device here is unnecessary (and would deadlock upon runtime suspend
* because it waits for polling to finish). We do however, want to
@@ -573,22 +564,23 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
ret = pm_runtime_get_sync(dev->dev);
if (ret < 0 && ret != -EACCES) {
pm_runtime_put_autosuspend(dev->dev);
+ nouveau_connector_set_edid(nv_connector, NULL);
return conn_status;
}
}
nv_encoder = nouveau_connector_ddc_detect(connector);
if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
+ struct edid *new_edid;
+
if ((vga_switcheroo_handler_flags() &
VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
nv_connector->type == DCB_CONNECTOR_LVDS)
- nv_connector->edid = drm_get_edid_switcheroo(connector,
- i2c);
+ new_edid = drm_get_edid_switcheroo(connector, i2c);
else
- nv_connector->edid = drm_get_edid(connector, i2c);
+ new_edid = drm_get_edid(connector, i2c);
- drm_connector_update_edid_property(connector,
- nv_connector->edid);
+ nouveau_connector_set_edid(nv_connector, new_edid);
if (!nv_connector->edid) {
NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
connector->name);
@@ -622,6 +614,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
conn_status = connector_status_connected;
drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
goto out;
+ } else {
+ nouveau_connector_set_edid(nv_connector, NULL);
}
nv_encoder = nouveau_connector_of_detect(connector);
@@ -646,10 +640,11 @@ detect_analog:
conn_status = connector_status_connected;
goto out;
}
-
}
out:
+ if (!nv_connector->edid)
+ drm_dp_cec_unset_edid(&nv_connector->aux);
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
@@ -664,18 +659,12 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
+ struct edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected;
- /* Cleanup the previous EDID block. */
- if (nv_connector->edid) {
- drm_connector_update_edid_property(connector, NULL);
- kfree(nv_connector->edid);
- nv_connector->edid = NULL;
- }
-
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
if (!nv_encoder)
- return connector_status_disconnected;
+ goto out;
/* Try retrieving EDID via DDC */
if (!drm->vbios.fp_no_ddc) {
@@ -694,7 +683,8 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
* valid - it's not (rh#613284)
*/
if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
- if ((nv_connector->edid = nouveau_acpi_edid(dev, connector))) {
+ edid = nouveau_acpi_edid(dev, connector);
+ if (edid) {
status = connector_status_connected;
goto out;
}
@@ -714,12 +704,10 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
* stored for the panel stored in them.
*/
if (!drm->vbios.fp_no_ddc) {
- struct edid *edid =
- (struct edid *)nouveau_bios_embedded_edid(dev);
+ edid = (struct edid *)nouveau_bios_embedded_edid(dev);
if (edid) {
- nv_connector->edid =
- kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
- if (nv_connector->edid)
+ edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+ if (edid)
status = connector_status_connected;
}
}
@@ -732,7 +720,7 @@ out:
status = connector_status_unknown;
#endif
- drm_connector_update_edid_property(connector, nv_connector->edid);
+ nouveau_connector_set_edid(nv_connector, edid);
nouveau_connector_set_encoder(connector, nv_encoder);
return status;
}
@@ -1150,59 +1138,39 @@ nouveau_connector_funcs_lvds = {
.early_unregister = nouveau_connector_early_unregister,
};
+void
+nouveau_connector_hpd(struct drm_connector *connector)
+{
+ struct nouveau_drm *drm = nouveau_drm(connector->dev);
+ u32 mask = drm_connector_mask(connector);
+
+ mutex_lock(&drm->hpd_lock);
+ if (!(drm->hpd_pending & mask)) {
+ drm->hpd_pending |= mask;
+ schedule_work(&drm->hpd_work);
+ }
+ mutex_unlock(&drm->hpd_lock);
+}
+
static int
nouveau_connector_hotplug(struct nvif_notify *notify)
{
struct nouveau_connector *nv_connector =
container_of(notify, typeof(*nv_connector), hpd);
struct drm_connector *connector = &nv_connector->base;
- struct nouveau_drm *drm = nouveau_drm(connector->dev);
+ struct drm_device *dev = connector->dev;
+ struct nouveau_drm *drm = nouveau_drm(dev);
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
- const char *name = connector->name;
- struct nouveau_encoder *nv_encoder;
- int ret;
bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
- NV_DEBUG(drm, "service %s\n", name);
- drm_dp_cec_irq(&nv_connector->aux);
- if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
- nv50_mstm_service(nv_encoder->dp.mstm);
-
+ nouveau_dp_irq(drm, nv_connector);
return NVIF_NOTIFY_KEEP;
}
- ret = pm_runtime_get(drm->dev->dev);
- if (ret == 0) {
- /* We can't block here if there's a pending PM request
- * running, as we'll deadlock nouveau_display_fini() when it
- * calls nvif_put() on our nvif_notify struct. So, simply
- * defer the hotplug event until the device finishes resuming
- */
- NV_DEBUG(drm, "Deferring HPD on %s until runtime resume\n",
- name);
- schedule_work(&drm->hpd_work);
-
- pm_runtime_put_noidle(drm->dev->dev);
- return NVIF_NOTIFY_KEEP;
- } else if (ret != 1 && ret != -EACCES) {
- NV_WARN(drm, "HPD on %s dropped due to RPM failure: %d\n",
- name, ret);
- return NVIF_NOTIFY_DROP;
- }
-
- if (!plugged)
- drm_dp_cec_unset_edid(&nv_connector->aux);
- NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
- if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
- if (!plugged)
- nv50_mstm_remove(nv_encoder->dp.mstm);
- }
-
- drm_helper_hpd_irq_event(connector->dev);
+ NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", connector->name);
+ nouveau_connector_hpd(connector);
- pm_runtime_mark_last_busy(drm->dev->dev);
- pm_runtime_put_autosuspend(drm->dev->dev);
return NVIF_NOTIFY_KEEP;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index d6de5cb8e223..d0b859c4a80e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -187,6 +187,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
struct drm_connector *
nouveau_connector_create(struct drm_device *, const struct dcb_output *);
+void nouveau_connector_hpd(struct drm_connector *connector);
extern int nouveau_tv_disable;
extern int nouveau_ignorelid;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 5f31b11ac2e7..bceb48a2dfca 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -457,16 +457,70 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
} \
} while(0)
+void
+nouveau_display_hpd_resume(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+
+ mutex_lock(&drm->hpd_lock);
+ drm->hpd_pending = ~0;
+ mutex_unlock(&drm->hpd_lock);
+
+ schedule_work(&drm->hpd_work);
+}
+
static void
nouveau_display_hpd_work(struct work_struct *work)
{
struct nouveau_drm *drm = container_of(work, typeof(*drm), hpd_work);
+ struct drm_device *dev = drm->dev;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ u32 pending;
+ bool changed = false;
+
+ pm_runtime_get_sync(dev->dev);
- pm_runtime_get_sync(drm->dev->dev);
+ mutex_lock(&drm->hpd_lock);
+ pending = drm->hpd_pending;
+ drm->hpd_pending = 0;
+ mutex_unlock(&drm->hpd_lock);
- drm_helper_hpd_irq_event(drm->dev);
+ /* Nothing to do, exit early without updating the last busy counter */
+ if (!pending)
+ goto noop;
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_connector_list_iter_begin(dev, &conn_iter);
+
+ nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
+ enum drm_connector_status old_status = connector->status;
+ u64 old_epoch_counter = connector->epoch_counter;
+
+ if (!(pending & drm_connector_mask(connector)))
+ continue;
+
+ connector->status = drm_helper_probe_detect(connector, NULL,
+ false);
+ if (old_epoch_counter == connector->epoch_counter)
+ continue;
+
+ changed = true;
+ drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n",
+ connector->base.id, connector->name,
+ drm_get_connector_status_name(old_status),
+ drm_get_connector_status_name(connector->status),
+ old_epoch_counter, connector->epoch_counter);
+ }
+
+ drm_connector_list_iter_end(&conn_iter);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ if (changed)
+ drm_kms_helper_hotplug_event(dev);
pm_runtime_mark_last_busy(drm->dev->dev);
+noop:
pm_runtime_put_sync(drm->dev->dev);
}
@@ -490,12 +544,11 @@ nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
*/
pm_runtime_put_autosuspend(drm->dev->dev);
} else if (ret == 0) {
- /* This may be the only indication we receive
- * of a connector hotplug on a runtime
- * suspended GPU, schedule hpd_work to check.
+ /* We've started resuming the GPU already, so
+ * it will handle scheduling a full reprobe
+ * itself
*/
NV_DEBUG(drm, "ACPI requested connector reprobe\n");
- schedule_work(&drm->hpd_work);
pm_runtime_put_noidle(drm->dev->dev);
} else {
NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n",
@@ -569,7 +622,7 @@ nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
cancel_work_sync(&drm->hpd_work);
drm_kms_helper_poll_disable(dev);
- disp->fini(dev, suspend);
+ disp->fini(dev, runtime, suspend);
}
static void
@@ -686,6 +739,7 @@ nouveau_display_create(struct drm_device *dev)
}
INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
+ mutex_init(&drm->hpd_lock);
#ifdef CONFIG_ACPI
drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
register_acpi_notifier(&drm->acpi_nb);
@@ -705,9 +759,10 @@ void
nouveau_display_destroy(struct drm_device *dev)
{
struct nouveau_display *disp = nouveau_display(dev);
+ struct nouveau_drm *drm = nouveau_drm(dev);
#ifdef CONFIG_ACPI
- unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
+ unregister_acpi_notifier(&drm->acpi_nb);
#endif
drm_kms_helper_poll_fini(dev);
@@ -719,6 +774,7 @@ nouveau_display_destroy(struct drm_device *dev)
nvif_disp_dtor(&disp->disp);
nouveau_drm(dev)->display = NULL;
+ mutex_destroy(&drm->hpd_lock);
kfree(disp);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index 6e0d900441d6..616c43427059 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -18,7 +18,7 @@ struct nouveau_display {
void *priv;
void (*dtor)(struct drm_device *);
int (*init)(struct drm_device *, bool resume, bool runtime);
- void (*fini)(struct drm_device *, bool suspend);
+ void (*fini)(struct drm_device *, bool suspend, bool runtime);
struct nvif_disp disp;
@@ -45,6 +45,7 @@ nouveau_display(struct drm_device *dev)
int nouveau_display_create(struct drm_device *dev);
void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev, bool resume, bool runtime);
+void nouveau_display_hpd_resume(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime);
int nouveau_display_suspend(struct drm_device *dev, bool runtime);
void nouveau_display_resume(struct drm_device *dev, bool runtime);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 8a0f7994e1ae..810bf6956568 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -36,50 +36,123 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)");
static int nouveau_mst = 1;
module_param_named(mst, nouveau_mst, int, 0400);
-static void
-nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_aux *aux, u8 *dpcd)
+static bool
+nouveau_dp_has_sink_count(struct drm_connector *connector,
+ struct nouveau_encoder *outp)
{
- struct nouveau_drm *drm = nouveau_drm(dev);
- u8 buf[3];
-
- if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
- return;
-
- if (!nvkm_rdaux(aux, DP_SINK_OUI, buf, 3))
- NV_DEBUG(drm, "Sink OUI: %02hx%02hx%02hx\n",
- buf[0], buf[1], buf[2]);
-
- if (!nvkm_rdaux(aux, DP_BRANCH_OUI, buf, 3))
- NV_DEBUG(drm, "Branch OUI: %02hx%02hx%02hx\n",
- buf[0], buf[1], buf[2]);
+ return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc);
+}
+static enum drm_connector_status
+nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
+ struct nouveau_encoder *outp)
+{
+ struct drm_connector *connector = &nv_connector->base;
+ struct drm_dp_aux *aux = &nv_connector->aux;
+ struct nv50_mstm *mstm = NULL;
+ enum drm_connector_status status = connector_status_disconnected;
+ int ret;
+ u8 *dpcd = outp->dp.dpcd;
+
+ ret = drm_dp_read_dpcd_caps(aux, dpcd);
+ if (ret < 0)
+ goto out;
+
+ ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd));
+ if (ret < 0)
+ goto out;
+
+ if (nouveau_mst) {
+ mstm = outp->dp.mstm;
+ if (mstm)
+ mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd);
+ }
+
+ if (nouveau_dp_has_sink_count(connector, outp)) {
+ ret = drm_dp_read_sink_count(aux);
+ if (ret < 0)
+ goto out;
+
+ outp->dp.sink_count = ret;
+
+ /*
+ * Dongle connected, but no display. Don't bother reading
+ * downstream port info
+ */
+ if (!outp->dp.sink_count)
+ return connector_status_disconnected;
+ }
+
+ ret = drm_dp_read_downstream_info(aux, dpcd,
+ outp->dp.downstream_ports);
+ if (ret < 0)
+ goto out;
+
+ status = connector_status_connected;
+out:
+ if (status != connector_status_connected) {
+ /* Clear any cached info */
+ outp->dp.sink_count = 0;
+ }
+ return status;
}
int
-nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
+nouveau_dp_detect(struct nouveau_connector *nv_connector,
+ struct nouveau_encoder *nv_encoder)
{
struct drm_device *dev = nv_encoder->base.base.dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nvkm_i2c_aux *aux;
- u8 dpcd[8];
- int ret;
-
- aux = nv_encoder->aux;
- if (!aux)
- return -ENODEV;
-
- ret = nvkm_rdaux(aux, DP_DPCD_REV, dpcd, sizeof(dpcd));
- if (ret)
- return ret;
+ struct drm_connector *connector = &nv_connector->base;
+ struct nv50_mstm *mstm = nv_encoder->dp.mstm;
+ enum drm_connector_status status;
+ u8 *dpcd = nv_encoder->dp.dpcd;
+ int ret = NOUVEAU_DP_NONE;
+
+ /* If we've already read the DPCD on an eDP device, we don't need to
+ * reread it as it won't change
+ */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
+ dpcd[DP_DPCD_REV] != 0)
+ return NOUVEAU_DP_SST;
- nv_encoder->dp.link_bw = 27000 * dpcd[1];
- nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
+ mutex_lock(&nv_encoder->dp.hpd_irq_lock);
+ if (mstm) {
+ /* If we're not ready to handle MST state changes yet, just
+ * report the last status of the connector. We'll reprobe it
+ * once we've resumed.
+ */
+ if (mstm->suspended) {
+ if (mstm->is_mst)
+ ret = NOUVEAU_DP_MST;
+ else if (connector->status ==
+ connector_status_connected)
+ ret = NOUVEAU_DP_SST;
+
+ goto out;
+ }
+ }
+
+ status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder);
+ if (status == connector_status_disconnected)
+ goto out;
+
+ /* If we're in MST mode, we're done here */
+ if (mstm && mstm->can_mst && mstm->is_mst) {
+ ret = NOUVEAU_DP_MST;
+ goto out;
+ }
+
+ nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE];
+ nv_encoder->dp.link_nr =
+ dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n",
- nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]);
+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw,
+ dpcd[DP_DPCD_REV]);
NV_DEBUG(drm, "encoder: %dx%d\n",
- nv_encoder->dcb->dpconf.link_nr,
- nv_encoder->dcb->dpconf.link_bw);
+ nv_encoder->dcb->dpconf.link_nr,
+ nv_encoder->dcb->dpconf.link_bw);
if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr)
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
@@ -87,23 +160,68 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw;
NV_DEBUG(drm, "maximum: %dx%d\n",
- nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
+
+ if (mstm && mstm->can_mst) {
+ ret = nv50_mstm_detect(nv_encoder);
+ if (ret == 1) {
+ ret = NOUVEAU_DP_MST;
+ goto out;
+ } else if (ret != 0) {
+ goto out;
+ }
+ }
+ ret = NOUVEAU_DP_SST;
+
+out:
+ if (mstm && !mstm->suspended && ret != NOUVEAU_DP_MST)
+ nv50_mstm_remove(mstm);
+
+ mutex_unlock(&nv_encoder->dp.hpd_irq_lock);
+ return ret;
+}
- nouveau_dp_probe_oui(dev, aux, dpcd);
+void nouveau_dp_irq(struct nouveau_drm *drm,
+ struct nouveau_connector *nv_connector)
+{
+ struct drm_connector *connector = &nv_connector->base;
+ struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
+ struct nv50_mstm *mstm;
+ int ret;
+ bool send_hpd = false;
- ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst);
- if (ret == 1)
- return NOUVEAU_DP_MST;
- if (ret == 0)
- return NOUVEAU_DP_SST;
- return ret;
+ if (!outp)
+ return;
+
+ mstm = outp->dp.mstm;
+ NV_DEBUG(drm, "service %s\n", connector->name);
+
+ mutex_lock(&outp->dp.hpd_irq_lock);
+
+ if (mstm && mstm->is_mst) {
+ if (!nv50_mstm_service(drm, nv_connector, mstm))
+ send_hpd = true;
+ } else {
+ drm_dp_cec_irq(&nv_connector->aux);
+
+ if (nouveau_dp_has_sink_count(connector, outp)) {
+ ret = drm_dp_read_sink_count(&nv_connector->aux);
+ if (ret != outp->dp.sink_count)
+ send_hpd = true;
+ if (ret >= 0)
+ outp->dp.sink_count = ret;
+ }
+ }
+
+ mutex_unlock(&outp->dp.hpd_irq_lock);
+
+ if (send_hpd)
+ nouveau_connector_hpd(connector);
}
/* TODO:
* - Use the minimum possible BPC here, once we add support for the max bpc
* property.
- * - Validate the mode against downstream port caps (see
- * drm_dp_downstream_max_clock())
* - Validate against the DP caps advertised by the GPU (we don't check these
* yet)
*/
@@ -114,15 +232,19 @@ nv50_dp_mode_valid(struct drm_connector *connector,
unsigned *out_clock)
{
const unsigned min_clock = 25000;
- unsigned max_clock, clock;
+ unsigned max_clock, ds_clock, clock;
enum drm_mode_status ret;
if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace)
return MODE_NO_INTERLACE;
max_clock = outp->dp.link_nr * outp->dp.link_bw;
- clock = mode->clock * (connector->display_info.bpc * 3) / 10;
+ ds_clock = drm_dp_downstream_max_clock(outp->dp.dpcd,
+ outp->dp.downstream_ports);
+ if (ds_clock)
+ max_clock = min(max_clock, ds_clock);
+ clock = mode->clock * (connector->display_info.bpc * 3) / 10;
ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock,
&clock);
if (out_clock)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 22d246acc5e5..42fc5c813a9b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -953,7 +953,7 @@ nouveau_pmops_resume(struct device *dev)
ret = nouveau_do_resume(drm_dev, false);
/* Monitors may have been connected / disconnected during suspend */
- schedule_work(&nouveau_drm(drm_dev)->hpd_work);
+ nouveau_display_hpd_resume(drm_dev);
return ret;
}
@@ -1036,7 +1036,7 @@ nouveau_pmops_runtime_resume(struct device *dev)
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
/* Monitors may have been connected / disconnected during suspend */
- schedule_work(&nouveau_drm(drm_dev)->hpd_work);
+ nouveau_display_hpd_resume(drm_dev);
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index f63ac72aa556..73ebf5fba2fc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -198,6 +198,8 @@ struct nouveau_drm {
struct nvbios vbios;
struct nouveau_display *display;
struct work_struct hpd_work;
+ struct mutex hpd_lock;
+ u32 hpd_pending;
struct work_struct fbcon_work;
int fbcon_new_state;
#ifdef CONFIG_ACPI
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index a72c412ac8b1..21937f1c7dd9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -33,6 +33,7 @@
#include <drm/drm_dp_mst_helper.h>
#include "dispnv04/disp.h"
struct nv50_head_atom;
+struct nouveau_connector;
#define NV_DPMS_CLEARED 0x80
@@ -64,6 +65,17 @@ struct nouveau_encoder {
struct nv50_mstm *mstm;
int link_nr;
int link_bw;
+
+ /* Protects DP state that needs to be accessed outside
+ * connector reprobing contexts
+ */
+ struct mutex hpd_irq_lock;
+
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
+ struct drm_dp_desc desc;
+
+ u8 sink_count;
} dp;
};
@@ -77,6 +89,21 @@ struct nouveau_encoder {
struct nv50_head_atom *, u8 proto, u8 depth);
};
+struct nv50_mstm {
+ struct nouveau_encoder *outp;
+
+ struct drm_dp_mst_topology_mgr mgr;
+
+ /* Protected under nouveau_encoder->dp.hpd_irq_lock */
+ bool can_mst;
+ bool is_mst;
+ bool suspended;
+
+ bool modified;
+ bool disabled;
+ int links;
+};
+
struct nouveau_encoder *
find_encoder(struct drm_connector *connector, int type);
@@ -100,20 +127,29 @@ get_slave_funcs(struct drm_encoder *enc)
/* nouveau_dp.c */
enum nouveau_dp_status {
+ NOUVEAU_DP_NONE,
NOUVEAU_DP_SST,
NOUVEAU_DP_MST,
};
-int nouveau_dp_detect(struct nouveau_encoder *);
+int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *);
+void nouveau_dp_irq(struct nouveau_drm *drm,
+ struct nouveau_connector *nv_connector);
enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *,
struct nouveau_encoder *,
const struct drm_display_mode *,
unsigned *clock);
struct nouveau_connector *
-nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
-
-int nv50_mstm_detect(struct nv50_mstm *, u8 dpcd[8], int allow);
-void nv50_mstm_remove(struct nv50_mstm *);
-void nv50_mstm_service(struct nv50_mstm *);
+nv50_outp_get_new_connector(struct nouveau_encoder *outp,
+ struct drm_atomic_state *state);
+struct nouveau_connector *
+nv50_outp_get_old_connector(struct nouveau_encoder *outp,
+ struct drm_atomic_state *state);
+
+int nv50_mstm_detect(struct nouveau_encoder *encoder);
+void nv50_mstm_remove(struct nv50_mstm *mstm);
+bool nv50_mstm_service(struct nouveau_drm *drm,
+ struct nouveau_connector *nv_connector,
+ struct nv50_mstm *mstm);
#endif /* __NOUVEAU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
index 23b62081e951..572547d1aa83 100644
--- a/drivers/gpu/drm/panel/panel-raydium-rm67191.c
+++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
@@ -552,8 +552,7 @@ static int rad_panel_probe(struct mipi_dsi_device *dsi)
panel->dsi = dsi;
dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
- MIPI_DSI_CLOCK_NON_CONTINUOUS;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO;
ret = of_property_read_u32(np, "video-mode", &video_mode);
if (!ret) {
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index f499de71ab3d..2be358fb46f7 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -659,6 +659,32 @@ static void panel_simple_shutdown(struct device *dev)
drm_panel_unprepare(&panel->base);
}
+static const struct drm_display_mode ampire_am_1280800n3tzqw_t00h_mode = {
+ .clock = 71100,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 40,
+ .hsync_end = 1280 + 40 + 80,
+ .htotal = 1280 + 40 + 80 + 40,
+ .vdisplay = 800,
+ .vsync_start = 800 + 3,
+ .vsync_end = 800 + 3 + 10,
+ .vtotal = 800 + 3 + 10 + 10,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static const struct panel_desc ampire_am_1280800n3tzqw_t00h = {
+ .modes = &ampire_am_1280800n3tzqw_t00h_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
.clock = 9000,
.hdisplay = 480,
@@ -3052,7 +3078,7 @@ static const struct drm_display_mode ortustech_com43h4m85ulc_mode = {
static const struct panel_desc ortustech_com43h4m85ulc = {
.modes = &ortustech_com43h4m85ulc_mode,
.num_modes = 1,
- .bpc = 8,
+ .bpc = 6,
.size = {
.width = 56,
.height = 93,
@@ -3875,6 +3901,9 @@ static const struct panel_desc arm_rtsm = {
static const struct of_device_id platform_of_match[] = {
{
+ .compatible = "ampire,am-1280800n3tzqw-t00h",
+ .data = &ampire_am_1280800n3tzqw_t00h,
+ }, {
.compatible = "ampire,am-480272h3tmqw-t01h",
.data = &ampire_am_480272h3tmqw_t01h,
}, {
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 3a4372ea74b9..74ad50c7491c 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -422,7 +422,6 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
*/
struct radeon_ttm_tt {
struct ttm_dma_tt ttm;
- struct radeon_device *rdev;
u64 offset;
uint64_t userptr;
@@ -525,6 +524,7 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
struct ttm_resource *bo_mem)
{
struct radeon_ttm_tt *gtt = (void*)ttm;
+ struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
uint32_t flags = RADEON_GART_PAGE_VALID | RADEON_GART_PAGE_READ |
RADEON_GART_PAGE_WRITE;
int r;
@@ -541,7 +541,7 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
}
if (ttm->caching_state == tt_cached)
flags |= RADEON_GART_PAGE_SNOOP;
- r = radeon_gart_bind(gtt->rdev, gtt->offset, ttm->num_pages,
+ r = radeon_gart_bind(rdev, gtt->offset, ttm->num_pages,
ttm->pages, gtt->ttm.dma_address, flags);
if (r) {
DRM_ERROR("failed to bind %lu pages at 0x%08X\n",
@@ -554,8 +554,9 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
static void radeon_ttm_backend_unbind(struct ttm_tt *ttm)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
+ struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
- radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages);
+ radeon_gart_unbind(rdev, gtt->offset, ttm->num_pages);
if (gtt->userptr)
radeon_ttm_tt_unpin_userptr(ttm);
@@ -594,7 +595,6 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
return NULL;
}
gtt->ttm.ttm.func = &radeon_backend_func;
- gtt->rdev = rdev;
if (ttm_dma_tt_init(&gtt->ttm, bo, page_flags)) {
kfree(gtt);
return NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index d12446cbcae0..81924b7acf5c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -474,9 +474,7 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
- reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
- SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
- SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
+ reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0;
if (sun4i_tcon_get_pixel_depth(encoder) == 24)
reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
else
diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig
index 67624423013a..b925b8b1da16 100644
--- a/drivers/gpu/drm/virtio/Kconfig
+++ b/drivers/gpu/drm/virtio/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_VIRTIO_GPU
tristate "Virtio GPU driver"
- depends on DRM && VIRTIO && MMU
+ depends on DRM && VIRTIO && VIRTIO_MENU && MMU
select DRM_KMS_HELPER
select DRM_GEM_SHMEM_HELPER
select VIRTIO_DMA_SHARED_BUFFER
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 4f3b07a32b60..eaecc5a6c5db 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -32,8 +32,6 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
src_offset = composer->offset
+ (i * composer->pitch)
+ (j * composer->cpp);
- /* XRGB format ignores Alpha channel */
- bitmap_clear(vaddr_out + src_offset, 24, 8);
crc = crc32_le(crc, vaddr_out + src_offset,
sizeof(u32));
}
@@ -42,27 +40,51 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
return crc;
}
+static u8 blend_channel(u8 src, u8 dst, u8 alpha)
+{
+ u32 pre_blend;
+ u8 new_color;
+
+ pre_blend = (src * 255 + dst * (255 - alpha));
+
+ /* Faster div by 255 */
+ new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
+
+ return new_color;
+}
+
+static void alpha_blending(const u8 *argb_src, u8 *argb_dst)
+{
+ u8 alpha;
+
+ alpha = argb_src[3];
+ argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
+ argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
+ argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
+ /* Opaque primary */
+ argb_dst[3] = 0xFF;
+}
+
/**
* blend - blend value at vaddr_src with value at vaddr_dst
* @vaddr_dst: destination address
* @vaddr_src: source address
- * @dest_composer: destination framebuffer's metadata
+ * @dst_composer: destination framebuffer's metadata
* @src_composer: source framebuffer's metadata
*
- * Blend value at vaddr_src with value at vaddr_dst.
- * Currently, this function write value of vaddr_src on value
- * at vaddr_dst using buffer's metadata to locate the new values
- * from vaddr_src and their destination at vaddr_dst.
- *
- * TODO: Use the alpha value to blend vaddr_src with vaddr_dst
- * instead of overwriting it.
+ * Blend the vaddr_src value with the vaddr_dst value using the pre-multiplied
+ * alpha blending equation, since DRM currently assumes that the pixel color
+ * values have already been pre-multiplied with the alpha channel values. See
+ * more drm_plane_create_blend_mode_property(). This function uses buffer's
+ * metadata to locate the new composite values at vaddr_dst.
*/
static void blend(void *vaddr_dst, void *vaddr_src,
- struct vkms_composer *dest_composer,
+ struct vkms_composer *dst_composer,
struct vkms_composer *src_composer)
{
int i, j, j_dst, i_dst;
int offset_src, offset_dst;
+ u8 *pixel_dst, *pixel_src;
int x_src = src_composer->src.x1 >> 16;
int y_src = src_composer->src.y1 >> 16;
@@ -77,15 +99,16 @@ static void blend(void *vaddr_dst, void *vaddr_src,
for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
- offset_dst = dest_composer->offset
- + (i_dst * dest_composer->pitch)
- + (j_dst++ * dest_composer->cpp);
+ offset_dst = dst_composer->offset
+ + (i_dst * dst_composer->pitch)
+ + (j_dst++ * dst_composer->cpp);
offset_src = src_composer->offset
+ (i * src_composer->pitch)
+ (j * src_composer->cpp);
- memcpy(vaddr_dst + offset_dst,
- vaddr_src + offset_src, sizeof(u32));
+ pixel_src = (u8 *)(vaddr_src + offset_src);
+ pixel_dst = (u8 *)(vaddr_dst + offset_dst);
+ alpha_blending(pixel_src, pixel_dst);
}
i_dst++;
}
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index ac85e17428f8..09c012d54d58 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -86,6 +86,11 @@ static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
struct vkms_output *output = &vkmsdev->output;
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+ if (!READ_ONCE(vblank->enabled)) {
+ *vblank_time = ktime_get();
+ return true;
+ }
+
*vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
if (WARN_ON(*vblank_time == vblank->time))