[Intel-gfx] [RFC v2 13/20] drm/i915/dp: Extract drm_dp_downstream_read_info()
We're going to be doing the same probing process in nouveau for determining downstream DP port capabilities, so let's deduplicate the work by moving i915's code for handling this into a shared helper: drm_dp_downstream_read_info(). Note that when we do this, we also do make some functional changes while we're at it: * We always clear the downstream port info before trying to read it, just to make things easier for the caller * We skip reading downstream port info if the DPCD indicates that we don't support downstream port info * We only read as many bytes as needed for the reported number of downstream ports, no sense in reading the whole thing every time v2: * Fixup logic for calculating the downstream port length to account for the fact that downstream port caps can be either 1 byte or 4 bytes long. We can actually skip fixing the max_clock/max_bpc helpers here since they all check for DP_DETAILED_CAP_INFO_AVAILABLE anyway. * Fix ret code check for drm_dp_dpcd_read Signed-off-by: Lyude Paul --- drivers/gpu/drm/drm_dp_helper.c | 46 + drivers/gpu/drm/i915/display/intel_dp.c | 14 ++-- include/drm/drm_dp_helper.h | 3 ++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4c21cf69dad5a..4f845995f1f66 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -423,6 +423,52 @@ 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; +} + +/** + * drm_dp_downstream_read_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 + * + * 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_downstream_read_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_downstream_read_info); + /** * drm_dp_downstream_max_clock() - extract branch device max * pixel rate for legacy VGA diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1e29d3a012856..984e49194ca31 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4685,18 +4685,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; + return drm_dp_downstream_read_info(&intel_dp->aux, intel_dp->dpcd, + intel_dp->downstream_ports) == 0; } static bool diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 5c28199248626..1349f16564ace 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1613,6 +1613,9 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, u8 real_edid_checksum); +int drm_dp_downstream_read_info(struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], +
[Intel-gfx] [RFC v2 03/20] drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c
Since this actually logs accesses, we should probably always be using this imho… Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c4e9c21d4dd2b..8db9216d52c69 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -42,16 +42,12 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, { struct drm_device *dev = nv_encoder->base.base.dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c_aux *aux; - u8 dpcd[8]; + struct drm_dp_aux *aux = &nv_connector->aux; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; int ret; - aux = nv_encoder->aux; - if (!aux) - return -ENODEV; - - ret = nvkm_rdaux(aux, DP_DPCD_REV, dpcd, sizeof(dpcd)); - if (ret) + ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); + if (ret != sizeof(dpcd)) return ret; nv_encoder->dp.link_bw = 27000 * dpcd[1]; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 14/20] drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation
This adds support for querying the maximum clock rate of a downstream port on a DisplayPort connection. Generally, downstream ports refer to active dongles which can have their own pixel clock limits. Note as well, we also start marking the connector as disconnected if we can't read the DPCD, since we wouldn't be able to do anything without DPCD access anyway. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 3 +++ drivers/gpu/drm/nouveau/nouveau_dp.c | 15 +++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 0d6879c532bec..c33b026c1f43f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1257,7 +1257,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; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 71d095409c90d..c200f197083f9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -61,6 +61,11 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, mstm->can_mst = drm_dp_has_mst(aux, dpcd); } + ret = drm_dp_downstream_read_info(aux, dpcd, + outp->dp.downstream_ports); + if (ret < 0) + return connector_status_disconnected; + return connector_status_connected; } @@ -176,8 +181,6 @@ void nouveau_dp_irq(struct nouveau_drm *drm, /* 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) */ @@ -188,15 +191,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_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index eef4643f5f982..c1924a4529a7b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -72,6 +72,7 @@ struct nouveau_encoder { struct mutex hpd_irq_lock; u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; } dp; }; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 01/20] drm/nouveau/kms: Fix some indenting in nouveau_dp_detect()
Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8a0f7994e1aeb..ee778ddc95fae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -76,10 +76,10 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) nv_encoder->dp.link_nr = dpcd[2] & 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[0]); 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,7 +87,7 @@ 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); nouveau_dp_probe_oui(dev, aux, dpcd); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 00/20] drm/dp, i915, nouveau: Cleanup nouveau HPD and add DP features from i915
To start off: this patch series is less work to review then it looks - most (but not all) of the nouveau related work has already been reviewed elsewhere. Most of the reason I'm asking for an RFC here is because this code pulls a lot of code out of i915 and into shared DP helpers. Anyway-nouveau's HPD related code has been collecting dust for a while. Other then the occasional runtime PM related and MST related fixes, we're missing a lot of nice things that have been added to DRM since this was originally written. Additionally, the code is just really unoptimized in general: * We handle connector probing in the same context that we handle short IRQs in for DP, which means connector probing could potentially block ESI handling for MST * When we receive a hotplug event from a connector, we reprobe every single connector instead of just the connector that was hotplugged * Additionally because of the above reason, combined with the fact I had the bad idea of reusing some of the MST locks when I last rewrote nouveau's DP MST detection, trying to handle any other events that require short HPD IRQs is a bit awkward to actually implement. * We don't actually properly check whether EDIDs change or not when reprobing, which means we basically send out a hotplug event every single time we receive one even if nothing has changed Additionally, the code for handling DP that we have in nouveau is also quite unoptimized in general, doesn't use a lot of helpers that have been added since it was written, and is also missing quite a number of features: * Downstream port capability probing * Extended DPRX cap parsing * SINK_COUNT handling for hpd on dongles Luckily for us - all of these are implemented in i915 already. Since there's no reason for us to reinvent the wheel, and having more shared code is always nice, I decided to take the opportunity to extract the code for all of these features from i915 into a set of core DP helpers, which both i915 and nouveau (and hopefully other drivers in the future) can use. As well, this patch series also addesses the other general connector probing related issues I mentioned above, along with rewriting how we handle MST probing so we don't hit any surprise locking design issues in the future. As a note - most of this work is motivated by the fact that I'm planning on adding max_bpc/output_bpc prop support, DSC support (for both MST and SST, along with proper helpers for handling bandwidth limitations and DSC), and fallback link retraining. I figured I might as clean this code up and implement missing DP features like the ones mentioned here before moving on to those tasks. Also, I'm intending for this patch series to get merged through drm-misc-next. Once that happens, upstream maintainers will probably have an easier time with merging if they pull the pending patches for nouveau from Ben's tree before merging drm-misc-next. Lyude Paul (20): drm/nouveau/kms: Fix some indenting in nouveau_dp_detect() drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc() drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new() drm/nouveau/kms: Search for encoders' connectors properly drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable() drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling drm/i915/dp: Extract drm_dp_has_mst() drm/nouveau/kms: Use new drm_dp_has_mst() helper for checking MST caps drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect() drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths drm/i915/dp: Extract drm_dp_downstream_read_info() drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation drm/i915/dp: Extract drm_dp_has_sink_count() drm/i915/dp: Extract drm_dp_get_sink_count() drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT drm/nouveau/kms: Don't change EDID when it hasn't actually changed drm/i915/dp: Extract drm_dp_read_dpcd_caps() drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() drivers/gpu/drm/drm_dp_helper.c | 167 +++ drivers/gpu/drm/i915/display/intel_dp.c | 124 ++-- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 24 +- drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 306 +++- drivers/gpu/drm/nouveau/nouveau_connector.c | 132 - drivers/gpu/drm/nouveau/nouveau_connector.h |
[Intel-gfx] [RFC v2 06/20] drm/nouveau/kms: Search for encoders' connectors properly
While the way we find the associated connector for an encoder is just fine for legacy modesetting, it's not correct for nv50+ since that uses atomic modesetting. For reference, see the drm_encoder kdocs. Fix this by removing nouveau_encoder_connector_get(), and replacing it with nv04_encoder_get_connector(), nv50_outp_get_old_connector(), and nv50_outp_get_new_connector(). v2: * Don't line-wrap for_each_(old|new)_connector_in_state in nv50_outp_get_(old|new)_connector() - sravn Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 18 + drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 85 + drivers/gpu/drm/nouveau/nouveau_connector.c | 14 drivers/gpu/drm/nouveau/nouveau_encoder.h | 6 +- 9 files changed, 103 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c index ffdd447d87068..22d10f3285597 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 f9f4482c79b54..42687ea2a4ca3 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 900ab69df7e8f..3f046b917c85c 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -35,6 +35,24 @@ #include +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) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index 495d3284e8766..5ace5e906949a 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 *); +s
[Intel-gfx] [RFC v2 07/20] drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable()
Just use drm_dp_dpcd_(readb|writeb)() so we get automatic DPCD logging Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 11 +++ 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 57102e1a173bc..75005268941b9 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1629,19 +1629,22 @@ nv50_sor_disable(struct drm_encoder *encoder, { 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); } } -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 04/20] drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c
No functional changes. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8db9216d52c69..4030806e3522b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -50,11 +50,13 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, if (ret != sizeof(dpcd)) return ret; - nv_encoder->dp.link_bw = 27000 * dpcd[1]; - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + 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); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 12/20] drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths
Currently we perform both short IRQ handling for DP, and connector reprobing in the HPD IRQ handler. However since we need to grab connection_mutex in order to reprobe a connector, in theory we could accidentally block ourselves from handling any short IRQs until after a modeset completes if a connector hotplug happens to occur in parallel with a modeset. I haven't seen this actually happen yet, but since we're cleaning up nouveau's hotplug handling code anyway and we already have a hpd worker, we can simply fix this by only relying on the HPD worker to actually reprobe connectors when we receive a HPD IRQ. We also add a mask to nouveau_drm to keep track of which connectors are waiting to be reprobed in response to an HPD IRQ. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 42 + drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 70 ++--- drivers/gpu/drm/nouveau/nouveau_display.h | 1 + drivers/gpu/drm/nouveau/nouveau_dp.c| 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + 7 files changed, 86 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4a29f691c08e4..637e91594fbe8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1138,6 +1138,20 @@ 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) { @@ -1147,8 +1161,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - int ret; bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { @@ -1156,31 +1168,9 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - } - - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); - - 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 d6de5cb8e2238..d0b859c4a80ea 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 13016769a194b..bceb48a2dfca6 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[] = { }
[Intel-gfx] [RFC v2 20/20] drm/nouveau/kms: Start using drm_dp_read_dpcd_caps()
Now that we've extracted i915's code for reading both the normal DPCD caps and extended DPCD caps into a shared helper, let's start using this in nouveau to enable us to start checking extended DPCD caps for free. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 14 ++ 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 89afc97ee2591..271a0a863a0e1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -55,15 +55,13 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, int ret; u8 *dpcd = outp->dp.dpcd; - ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); - if (ret == DP_RECEIVER_CAP_SIZE && dpcd[DP_DPCD_REV]) { - ret = drm_dp_read_desc(aux, &outp->dp.desc, - drm_dp_is_branch(dpcd)); - if (ret < 0) - goto out; - } else { + 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; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 18/20] drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Currently in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds(), we start the connector probing process by releasing the previous EDID and informing DRM of the change. However, since commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector") drm_connector_update_edid_property() actually checks whether the new EDID we've specified is different from the previous one, and updates the connector's epoch accordingly if it is. But, because we always set the EDID to NULL first in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds() we end up making DRM think that the EDID changes every single time we do a connector probe - which isn't needed. So, let's fix this by not clearing the EDID at the start of the connector probing process, and instead simply changing or removing it once near the end of the probing process. This will help prevent us from sending unneeded hotplug events to userspace when nothing has actually changed. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 54 ++--- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 637e91594fbe8..49dd0cbc332ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -528,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) { @@ -541,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 @@ -560,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); @@ -609,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); @@ -652,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_connec
[Intel-gfx] [RFC v2 09/20] drm/i915/dp: Extract drm_dp_has_mst()
Just a tiny drive-by cleanup, we can consolidate i915's code for checking for MST support into a helper to be shared across drivers. Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/i915/display/intel_dp.c | 18 ++ include/drm/drm_dp_mst_helper.h | 22 ++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 79c27f91f42c0..1e29d3a012856 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4699,20 +4699,6 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) 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; -} - static bool intel_dp_can_mst(struct intel_dp *intel_dp) { @@ -4720,7 +4706,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_has_mst(&intel_dp->aux, intel_dp->dpcd); } static void @@ -4729,7 +4715,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_has_mst(&intel_dp->aux, intel_dp->dpcd); drm_dbg_kms(&i915->drm, "[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n", diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 8b9eb4db3381c..2d8983a713e8c 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -911,4 +911,26 @@ __drm_dp_mst_state_iter_get(struct drm_atomic_state *state, for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i))) +/** + * drm_dp_has_mst() - 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 + */ +static inline bool +drm_dp_has_mst(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); +} + #endif -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 08/20] drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling
First some backstory here: Currently, we keep track of whether or not we've enabled MST or not by trying to piggy-back off the MST helpers. This means that in order to check whether MST is enabled or not, we actually need to grab drm_dp_mst_topology_mgr.lock. Back when I originally wrote this, I did this piggy-backing with the intention that I'd eventually be teaching our MST helpers how to recover when an MST device has stopped responding, which in turn would require the MST helpers having a way of disabling MST independently of the driver. Note that this was before I reworked locking in the MST helpers, so at the time we were sticking random things under &mgr->lock - which grabbing this lock was meant to protect against. This never came to fruition because doing such a reset safely turned out to be a lot more painful and impossible then it sounds, and also just risks us working around issues with our MST handlers that should be properly fixed instead. Even if it did though, simply calling drm_dp_mst_topology_mgr_set_mst() from the MST helpers (with the exception of when we're tearing down our MST managers, that's always OK) wouldn't have been a bad idea, since drivers like nouveau and i915 need to do their own book keeping immediately after disabling MST. So-implementing that would likely require adding a hook for helper-triggered MST disables anyway. So, fast forward to now - we want to start adding support for all of the miscellaneous bits of the DP protocol (for both SST and MST) we're missing before moving on to supporting more complicated features like supporting different BPP values on MST, DSC, etc. Since many of these features only exist on SST and make use of DP HPD IRQs, we want to be able to atomically check whether we're servicing an MST IRQ or SST IRQ in nouveau_connector_hotplug(). Currently we literally don't do this at all, and just handle any kind of possible DP IRQ we could get including ESIs - even if MST isn't actually enabled. This would be very complicated and difficult to fix if we need to hold &mgr->lock while handling SST IRQs to ensure that the MST topology state doesn't change under us. What we really want here is to do our own tracking of whether MST is enabled or not, similar to drivers like i915, and define our own locking order to decomplicate things and avoid hitting locking issues in the future. So, let's do this by refactoring our MST probing/enabling code to use our own MST bookkeeping, along with adding a lock for protecting DP state that needs to be checked outside of our connector probing functions. While we're at it, we also remove a bunch of unneeded steps we perform when probing/enabling MST: * Enabling bits in MSTM_CTRL before calling drm_dp_mst_topology_mgr_set_mst(). I don't think these ever actually did anything, since the nvif methods for enabling MST don't actually do anything DPCD related and merely indicate to nvkm that we've turned on MST. * Checking the MSTM_CTRL bit is intact when checking the state of an enabled MST topology in nv50_mstm_detect(). I just added this to be safe originally, but now that we try reading the DPCD when probing DP connectors it shouldn't be needed as that will abort our hotplug probing if the device was removed well before we start checking for MST.. * All of the duplicate DPCD version checks. This leaves us with much nicer looking code, a much more sensible locking scheme, and an easy way of checking whether MST is enabled or not for handling DP HPD IRQs. v2: * Get rid of accidental newlines Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/disp.c | 6 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 196 +--- drivers/gpu/drm/nouveau/nouveau_connector.c | 14 +- drivers/gpu/drm/nouveau/nouveau_display.c | 2 +- drivers/gpu/drm/nouveau/nouveau_display.h | 2 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 132 +++-- drivers/gpu/drm/nouveau/nouveau_encoder.h | 33 +++- 7 files changed, 245 insertions(+), 140 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 3f046b917c85c..3ee836dc5058f 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -54,8 +54,9 @@ nv04_encoder_get_connector(struct nouveau_encoder *encoder) } 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; @@ -67,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(&
[Intel-gfx] [RFC v2 11/20] drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect()
For whatever reason we currently unset the EDID for DP CEC support when responding to the connector being unplugged, instead of just doing it in nouveau_connector_detect() where we set the CEC EDID. This isn't really needed and could even potentially cause us to forget to unset the EDID if the connector is removed without a corresponding hpd event, so let's fix that. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index b90591114faaf..4a29f691c08e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -633,10 +633,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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); @@ -1174,8 +1175,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) return NVIF_NOTIFY_DROP; } - if (!plugged) - drm_dp_cec_unset_edid(&nv_connector->aux); NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); drm_helper_hpd_irq_event(connector->dev); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 19/20] drm/i915/dp: Extract drm_dp_read_dpcd_caps()
Since DP 1.3, it's been possible for DP receivers to specify an additional set of DPCD capabilities, which can take precedence over the capabilities reported at DP_DPCD_REV. Basically any device supporting DP is going to need to read these in an identical manner, in particular nouveau, so let's go ahead and just move this code out of i915 into a shared DRM DP helper that we can use in other drivers. v2: * Remove redundant dpcd[DP_DPCD_REV] == 0 check * Fix drm_dp_dpcd_read() ret checks Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 77 + drivers/gpu/drm/i915/display/intel_dp.c | 60 +--- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- include/drm/drm_dp_helper.h | 3 + 5 files changed, 83 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 67ad05eb05b7e..9c99d21b42c15 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -433,6 +433,83 @@ static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 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 h could be at a value less than +* the true capability of the panel. The only way to check is to +* then compare h 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_downstream_read_info() - read DPCD downstream port info if available * @aux: DisplayPort AUX channel diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4337321a3be4f..fb7872e2a0b93 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 h could be at a value less than -* the true capability of the panel. The only way to check is to -* then compare h 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, -
[Intel-gfx] [RFC v2 15/20] drm/i915/dp: Extract drm_dp_has_sink_count()
Since other drivers are also going to need to be aware of the sink count in order to do proper dongle detection, we might as well steal i915's DP_SINK_COUNT helpers and move them into DRM helpers so that other dirvers can use them as well. Note that this also starts using intel_dp_has_sink_count() in intel_dp_detect_dpcd(), which is a functional change. Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 21 - include/drm/drm_dp_helper.h | 8 +++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4f845995f1f66..863e0babc1903 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -714,6 +714,28 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_dp_set_subconnector_property); +/** + * drm_dp_has_sink_count() - 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 + * + * Returns: %True if the (e)DP connector has a valid sink count that should + * be probed, %false otherwise. + */ +bool drm_dp_has_sink_count(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_has_sink_count); + /* * I2C-over-AUX implementation */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 984e49194ca31..35a4779a442e2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4634,6 +4634,16 @@ 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_has_sink_count(&intel_dp->attached_connector->base, +intel_dp->dpcd, +&intel_dp->desc); +} static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) @@ -4653,13 +4663,7 @@ 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)) { + if (intel_dp_has_sink_count(intel_dp)) { u8 count; ssize_t r; @@ -5939,9 +5943,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/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 1349f16564ace..a1413a531eaf4 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1631,6 +1631,11 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector, const u8 *dpcd, const u8 port_cap[4]); +struct drm_dp_desc; +bool drm_dp_has_sink_count(struct drm_connector *connector, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const struct drm_dp_desc *desc); + void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); @@ -1689,7 +1694,8 @@ enum drm_dp_quirk { * @DP_DPCD_QUIRK_NO_SINK_COUNT: * * The device does not set SINK_COUNT to a non-zero value. -* The driver should ignore SINK_COUNT during detection. +* The driver should ignore SINK_COUNT during detection. Note that +
[Intel-gfx] [RFC v2 17/20] drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT
This is another bit that we never implemented for nouveau: dongle detection. When a "dongle", e.g. an active display adaptor, is hooked up to the system and causes an HPD to be fired, we don't actually know whether or not there's anything plugged into the dongle without checking the sink count. As a result, plugging in a dongle without anything plugged into it currently results in a bogus EDID retrieval error in the kernel log. Additionally, most dongles won't send another long HPD signal if the user suddenly plugs something in, they'll only send a short HPD IRQ with the expectation that the source will check the sink count and reprobe the connector if it's changed - something we don't actually do. As a result, nothing will happen if the user plugs the dongle in before plugging something into the dongle. So, let's fix this by checking the sink count in both nouveau_dp_probe_dpcd() and nouveau_dp_irq(), and reprobing the connector if things change. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 54 --- drivers/gpu/drm/nouveau/nouveau_encoder.h | 2 + 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c200f197083f9..89afc97ee2591 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,12 +36,22 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)"); static int nouveau_mst = 1; module_param_named(mst, nouveau_mst, int, 0400); +static bool +nouveau_dp_has_sink_count(struct drm_connector *connector, + struct nouveau_encoder *outp) +{ + return drm_dp_has_sink_count(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; @@ -50,9 +60,9 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd)); if (ret < 0) - return connector_status_disconnected; + goto out; } else { - return connector_status_disconnected; + goto out; } if (nouveau_mst) { @@ -61,12 +71,33 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, mstm->can_mst = drm_dp_has_mst(aux, dpcd); } + if (nouveau_dp_has_sink_count(connector, outp)) { + ret = drm_dp_get_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_downstream_read_info(aux, dpcd, outp->dp.downstream_ports); if (ret < 0) - return connector_status_disconnected; + goto out; - return connector_status_connected; + status = connector_status_connected; +out: + if (status != connector_status_connected) { + /* Clear any cached info */ + outp->dp.sink_count = 0; + } + return status; } int @@ -159,6 +190,8 @@ void nouveau_dp_irq(struct nouveau_drm *drm, 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; if (!outp) return; @@ -170,12 +203,23 @@ void nouveau_dp_irq(struct nouveau_drm *drm, if (mstm && mstm->is_mst) { if (!nv50_mstm_service(drm, nv_connector, mstm)) - nouveau_connector_hpd(connector); + send_hpd = true; } else { drm_dp_cec_irq(&nv_connector->aux); + + if (nouveau_dp_has_sink_count(connector, outp)) { + ret = drm_dp_get_sink_count(&nv_connector->aux); + if (ret != outp->dp.sink_count) + send_hpd = true; + if (ret
[Intel-gfx] [RFC v2 16/20] drm/i915/dp: Extract drm_dp_get_sink_count()
And of course, we'll also need to read the sink count from other drivers as well if we're checking whether or not it's supported. So, let's extract the code for this into another helper. v2: * Fix drm_dp_dpcd_readb() ret check * Add back comment and move back sink_count assignment in intel_dp_get_dpcd() Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 11 +-- include/drm/drm_dp_helper.h | 1 + 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 863e0babc1903..67ad05eb05b7e 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -736,6 +736,28 @@ bool drm_dp_has_sink_count(struct drm_connector *connector, } EXPORT_SYMBOL(drm_dp_has_sink_count); +/** + * drm_dp_get_sink_count() - Retrieve the sink count for a given sink + * @aux: The DP AUX channel to use + * + * Returns: The current sink count reported by @aux, or a negative error code + * otherwise. + */ +int drm_dp_get_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_get_sink_count); + /* * I2C-over-AUX implementation */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 35a4779a442e2..4337321a3be4f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4648,6 +4648,8 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + int ret; + if (!intel_dp_read_dpcd(intel_dp)) return false; @@ -4664,11 +4666,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } if (intel_dp_has_sink_count(intel_dp)) { - u8 count; - ssize_t r; - - r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count); - if (r < 1) + ret = drm_dp_get_sink_count(&intel_dp->aux); + if (ret < 0) return false; /* @@ -4676,7 +4675,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 diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index a1413a531eaf4..0c141fc81aaa8 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1635,6 +1635,7 @@ struct drm_dp_desc; bool drm_dp_has_sink_count(struct drm_connector *connector, const u8 dpcd[DP_RECEIVER_CAP_SIZE], const struct drm_dp_desc *desc); +int drm_dp_get_sink_count(struct drm_dp_aux *aux); void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v2 02/20] drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc()
Noticed this while going through our DP code - we use an open-coded version of drm_dp_read_desc() instead of just using the helper, so change that. This will also let us use quirks in the future if we end up needing them. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_dp.c| 30 +++-- drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 7674025a4bfe8..e12957e6faa7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -435,7 +435,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) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ee778ddc95fae..c4e9c21d4dd2b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,27 +36,9 @@ 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) -{ - 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]); - -} - 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); @@ -89,7 +71,13 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); - nouveau_dp_probe_oui(dev, aux, dpcd); + ret = drm_dp_read_desc(&nv_connector->aux, &nv_encoder->dp.desc, + drm_dp_is_branch(dpcd)); + if (ret) { + NV_ERROR(drm, "Failed to read DP descriptor on %s: %d\n", +nv_connector->base.name, ret); + return ret; + } ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst); if (ret == 1) diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index a72c412ac8b14..6424cdcb4913f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -33,6 +33,7 @@ #include #include "dispnv04/disp.h" struct nv50_head_atom; +struct nouveau_connector; #define NV_DPMS_CLEARED 0x80 @@ -64,6 +65,7 @@ struct nouveau_encoder { struct nv50_mstm *mstm; int link_nr; int link_bw; + struct drm_dp_desc desc; } dp; }; @@ -104,7 +106,7 @@ enum nouveau_dp_status { NOUVEAU_DP_MST, }; -int nouveau_dp_detect(struct nouveau_encoder *); +int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, struct nouveau_encoder *, const struct drm_display_mode *, -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [RFC 13/20] drm/i915/dp: Extract drm_dp_downstream_read_info()
On Fri, 2020-08-21 at 01:37 +0300, Imre Deak wrote: > On Wed, Aug 19, 2020 at 05:34:15PM -0400, Lyude Paul wrote: > > (adding Ville and Imre to the cc here, they might be interested to know > > about > > this, comments down below) > > > > On Wed, 2020-08-19 at 11:15 -0400, Sean Paul wrote: > > > On Tue, Aug 11, 2020 at 04:04:50PM -0400, Lyude Paul wrote: > > > > We're going to be doing the same probing process in nouveau for > > > > determining downstream DP port capabilities, so let's deduplicate the > > > > work by moving i915's code for handling this into a shared helper: > > > > drm_dp_downstream_read_info(). > > > > > > > > Note that when we do this, we also do make some functional changes while > > > > we're at it: > > > > * We always clear the downstream port info before trying to read it, > > > > just to make things easier for the caller > > > > * We skip reading downstream port info if the DPCD indicates that we > > > > don't support downstream port info > > > > * We only read as many bytes as needed for the reported number of > > > > downstream ports, no sense in reading the whole thing every time > > > > > > > > Signed-off-by: Lyude Paul > > > > --- > > > > drivers/gpu/drm/drm_dp_helper.c | 32 + > > > > drivers/gpu/drm/i915/display/intel_dp.c | 14 ++- > > > > include/drm/drm_dp_helper.h | 3 +++ > > > > 3 files changed, 37 insertions(+), 12 deletions(-) > > > > > > > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > > > > b/drivers/gpu/drm/drm_dp_helper.c > > > > index 4c21cf69dad5a..9703b33599c3b 100644 > > > > --- a/drivers/gpu/drm/drm_dp_helper.c > > > > +++ b/drivers/gpu/drm/drm_dp_helper.c > > > > @@ -423,6 +423,38 @@ bool drm_dp_send_real_edid_checksum(struct > > > > drm_dp_aux > > > > *aux, > > > > } > > > > EXPORT_SYMBOL(drm_dp_send_real_edid_checksum); > > > > > > > > +/** > > > > + * drm_dp_downstream_read_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 > > > > + * > > > > + * 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_downstream_read_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 = (dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK) * > > > > 4; > > > > > > I'm having a hard time rationalizing DP_MAX_DOWNSTREAM_PORTS being 16, but > > > only > > > having 4 ports worth of data in the DP_DOWNSTREAM_PORT_* registers. Do you > > > know > > > what's supposed to happen if dpcd[DP_DOWN_STREAM_PORT_COUNT] is > 4? > > > > > ok!! Taking a lesson from our available_pbn/full_pbn confusion in the past, > > I > > squinted very hard at the specification and eventually found something that > > I > > think clears this up. Surprise - we definitely had this implemented > > incorrectly > > in i915 > > To me it looks correct, only DFP0's cap info is used, by also handling > the DP_DETAILED_CAP_INFO_AVAILABLE=0/1 cases. Ended up realizing this right after I sent this version of the RFC - yeah, it definitely shouldn't be causing any real problems as of now > > The wording is a bit unclear, but as I understand the Standard o
[Intel-gfx] [RFC v3] drm/nouveau/kms: Search for encoders' connectors properly
While the way we find the associated connector for an encoder is just fine for legacy modesetting, it's not correct for nv50+ since that uses atomic modesetting. For reference, see the drm_encoder kdocs. Fix this by removing nouveau_encoder_connector_get(), and replacing it with nv04_encoder_get_connector(), nv50_outp_get_old_connector(), and nv50_outp_get_new_connector(). v2: * Don't line-wrap for_each_(old|new)_connector_in_state in nv50_outp_get_(old|new)_connector() - sravn v3: * Fix potential uninitialized usage of nv_connector (needs to be initialized to NULL at the start). Thanks kernel test robot! Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 18 + drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 87 + drivers/gpu/drm/nouveau/nouveau_connector.c | 14 drivers/gpu/drm/nouveau/nouveau_encoder.h | 6 +- 9 files changed, 104 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c index ffdd447d87068..22d10f3285597 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 f9f4482c79b54..42687ea2a4ca3 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 900ab69df7e8f..3f046b917c85c 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -35,6 +35,24 @@ #include +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) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index 495d3284e8766..5ace5e906949a 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_
[Intel-gfx] [RFC v4 00/20] drm/dp, i915, nouveau: Cleanup nouveau HPD and add DP features from i915
Most of the reason I'm asking for an RFC here is because this code pulls a lot of code out of i915 and into shared DP helpers. Anyway-nouveau's HPD related code has been collecting dust for a while. Other then the occasional runtime PM related and MST related fixes, we're missing a lot of nice things that have been added to DRM since this was originally written. Additionally, the code is just really unoptimized in general: * We handle connector probing in the same context that we handle short IRQs in for DP, which means connector probing could potentially block ESI handling for MST * When we receive a hotplug event from a connector, we reprobe every single connector instead of just the connector that was hotplugged * Additionally because of the above reason, combined with the fact I had the bad idea of reusing some of the MST locks when I last rewrote nouveau's DP MST detection, trying to handle any other events that require short HPD IRQs is a bit awkward to actually implement. * We don't actually properly check whether EDIDs change or not when reprobing, which means we basically send out a hotplug event every single time we receive one even if nothing has changed Additionally, the code for handling DP that we have in nouveau is also quite unoptimized in general, doesn't use a lot of helpers that have been added since it was written, and is also missing quite a number of features: * Downstream port capability probing * Extended DPRX cap parsing * SINK_COUNT handling for hpd on dongles Luckily for us - all of these are implemented in i915 already. Since there's no reason for us to reinvent the wheel, and having more shared code is always nice, I decided to take the opportunity to extract the code for all of these features from i915 into a set of core DP helpers, which both i915 and nouveau (and hopefully other drivers in the future) can use. As well, this patch series also addesses the other general connector probing related issues I mentioned above, along with rewriting how we handle MST probing so we don't hit any surprise locking design issues in the future. As a note - most of this work is motivated by the fact that I'm planning on adding max_bpc/output_bpc prop support, DSC support (for both MST and SST, along with proper helpers for handling bandwidth limitations and DSC), and fallback link retraining. I figured I might as clean this code up and implement missing DP features like the ones mentioned here before moving on to those tasks. Lyude Paul (20): drm/nouveau/kms: Fix some indenting in nouveau_dp_detect() drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc() drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new() drm/nouveau/kms: Search for encoders' connectors properly drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable() drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling drm/i915/dp: Extract drm_dp_has_mst() drm/nouveau/kms: Use new drm_dp_has_mst() helper for checking MST caps drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect() drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths drm/i915/dp: Extract drm_dp_downstream_read_info() drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation drm/i915/dp: Extract drm_dp_has_sink_count() drm/i915/dp: Extract drm_dp_get_sink_count() drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT drm/nouveau/kms: Don't change EDID when it hasn't actually changed drm/i915/dp: Extract drm_dp_read_dpcd_caps() drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() drivers/gpu/drm/drm_dp_helper.c | 167 +++ drivers/gpu/drm/i915/display/intel_dp.c | 124 ++-- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 24 +- drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 305 +++- drivers/gpu/drm/nouveau/nouveau_connector.c | 132 - drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 72 - drivers/gpu/drm/nouveau/nouveau_display.h | 3 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 211 +++--- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_encoder.h | 48 ++- include/drm/drm_dp_helper.h | 15 +- include/drm/drm_dp_ms
[Intel-gfx] [RFC v4 01/20] drm/nouveau/kms: Fix some indenting in nouveau_dp_detect()
Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8a0f7994e1aeb..ee778ddc95fae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -76,10 +76,10 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) nv_encoder->dp.link_nr = dpcd[2] & 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[0]); 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,7 +87,7 @@ 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); nouveau_dp_probe_oui(dev, aux, dpcd); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 04/20] drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c
No functional changes. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8db9216d52c69..4030806e3522b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -50,11 +50,13 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, if (ret != sizeof(dpcd)) return ret; - nv_encoder->dp.link_bw = 27000 * dpcd[1]; - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + 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); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 02/20] drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc()
Noticed this while going through our DP code - we use an open-coded version of drm_dp_read_desc() instead of just using the helper, so change that. This will also let us use quirks in the future if we end up needing them. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_dp.c| 30 +++-- drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 7674025a4bfe8..e12957e6faa7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -435,7 +435,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) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ee778ddc95fae..c4e9c21d4dd2b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,27 +36,9 @@ 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) -{ - 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]); - -} - 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); @@ -89,7 +71,13 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); - nouveau_dp_probe_oui(dev, aux, dpcd); + ret = drm_dp_read_desc(&nv_connector->aux, &nv_encoder->dp.desc, + drm_dp_is_branch(dpcd)); + if (ret) { + NV_ERROR(drm, "Failed to read DP descriptor on %s: %d\n", +nv_connector->base.name, ret); + return ret; + } ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst); if (ret == 1) diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index a72c412ac8b14..6424cdcb4913f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -33,6 +33,7 @@ #include #include "dispnv04/disp.h" struct nv50_head_atom; +struct nouveau_connector; #define NV_DPMS_CLEARED 0x80 @@ -64,6 +65,7 @@ struct nouveau_encoder { struct nv50_mstm *mstm; int link_nr; int link_bw; + struct drm_dp_desc desc; } dp; }; @@ -104,7 +106,7 @@ enum nouveau_dp_status { NOUVEAU_DP_MST, }; -int nouveau_dp_detect(struct nouveau_encoder *); +int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, struct nouveau_encoder *, const struct drm_display_mode *, -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 03/20] drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c
Since this actually logs accesses, we should probably always be using this imho… Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c4e9c21d4dd2b..8db9216d52c69 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -42,16 +42,12 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, { struct drm_device *dev = nv_encoder->base.base.dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c_aux *aux; - u8 dpcd[8]; + struct drm_dp_aux *aux = &nv_connector->aux; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; int ret; - aux = nv_encoder->aux; - if (!aux) - return -ENODEV; - - ret = nvkm_rdaux(aux, DP_DPCD_REV, dpcd, sizeof(dpcd)); - if (ret) + ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); + if (ret != sizeof(dpcd)) return ret; nv_encoder->dp.link_bw = 27000 * dpcd[1]; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 14/20] drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation
This adds support for querying the maximum clock rate of a downstream port on a DisplayPort connection. Generally, downstream ports refer to active dongles which can have their own pixel clock limits. Note as well, we also start marking the connector as disconnected if we can't read the DPCD, since we wouldn't be able to do anything without DPCD access anyway. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 3 +++ drivers/gpu/drm/nouveau/nouveau_dp.c | 15 +++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 8e1effb10425d..d2141ca16107b 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1258,7 +1258,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; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 71d095409c90d..c200f197083f9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -61,6 +61,11 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, mstm->can_mst = drm_dp_has_mst(aux, dpcd); } + ret = drm_dp_downstream_read_info(aux, dpcd, + outp->dp.downstream_ports); + if (ret < 0) + return connector_status_disconnected; + return connector_status_connected; } @@ -176,8 +181,6 @@ void nouveau_dp_irq(struct nouveau_drm *drm, /* 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) */ @@ -188,15 +191,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_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index eef4643f5f982..c1924a4529a7b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -72,6 +72,7 @@ struct nouveau_encoder { struct mutex hpd_irq_lock; u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; } dp; }; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 17/20] drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT
This is another bit that we never implemented for nouveau: dongle detection. When a "dongle", e.g. an active display adaptor, is hooked up to the system and causes an HPD to be fired, we don't actually know whether or not there's anything plugged into the dongle without checking the sink count. As a result, plugging in a dongle without anything plugged into it currently results in a bogus EDID retrieval error in the kernel log. Additionally, most dongles won't send another long HPD signal if the user suddenly plugs something in, they'll only send a short HPD IRQ with the expectation that the source will check the sink count and reprobe the connector if it's changed - something we don't actually do. As a result, nothing will happen if the user plugs the dongle in before plugging something into the dongle. So, let's fix this by checking the sink count in both nouveau_dp_probe_dpcd() and nouveau_dp_irq(), and reprobing the connector if things change. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 54 --- drivers/gpu/drm/nouveau/nouveau_encoder.h | 2 + 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c200f197083f9..89afc97ee2591 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,12 +36,22 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)"); static int nouveau_mst = 1; module_param_named(mst, nouveau_mst, int, 0400); +static bool +nouveau_dp_has_sink_count(struct drm_connector *connector, + struct nouveau_encoder *outp) +{ + return drm_dp_has_sink_count(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; @@ -50,9 +60,9 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd)); if (ret < 0) - return connector_status_disconnected; + goto out; } else { - return connector_status_disconnected; + goto out; } if (nouveau_mst) { @@ -61,12 +71,33 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, mstm->can_mst = drm_dp_has_mst(aux, dpcd); } + if (nouveau_dp_has_sink_count(connector, outp)) { + ret = drm_dp_get_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_downstream_read_info(aux, dpcd, outp->dp.downstream_ports); if (ret < 0) - return connector_status_disconnected; + goto out; - return connector_status_connected; + status = connector_status_connected; +out: + if (status != connector_status_connected) { + /* Clear any cached info */ + outp->dp.sink_count = 0; + } + return status; } int @@ -159,6 +190,8 @@ void nouveau_dp_irq(struct nouveau_drm *drm, 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; if (!outp) return; @@ -170,12 +203,23 @@ void nouveau_dp_irq(struct nouveau_drm *drm, if (mstm && mstm->is_mst) { if (!nv50_mstm_service(drm, nv_connector, mstm)) - nouveau_connector_hpd(connector); + send_hpd = true; } else { drm_dp_cec_irq(&nv_connector->aux); + + if (nouveau_dp_has_sink_count(connector, outp)) { + ret = drm_dp_get_sink_count(&nv_connector->aux); + if (ret != outp->dp.sink_count) + send_hpd = true; + if (ret
[Intel-gfx] [RFC v4 09/20] drm/i915/dp: Extract drm_dp_has_mst()
Just a tiny drive-by cleanup, we can consolidate i915's code for checking for MST support into a helper to be shared across drivers. Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/i915/display/intel_dp.c | 18 ++ include/drm/drm_dp_mst_helper.h | 22 ++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 79c27f91f42c0..1e29d3a012856 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4699,20 +4699,6 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) 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; -} - static bool intel_dp_can_mst(struct intel_dp *intel_dp) { @@ -4720,7 +4706,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_has_mst(&intel_dp->aux, intel_dp->dpcd); } static void @@ -4729,7 +4715,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_has_mst(&intel_dp->aux, intel_dp->dpcd); drm_dbg_kms(&i915->drm, "[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n", diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 8b9eb4db3381c..2d8983a713e8c 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -911,4 +911,26 @@ __drm_dp_mst_state_iter_get(struct drm_atomic_state *state, for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i))) +/** + * drm_dp_has_mst() - 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 + */ +static inline bool +drm_dp_has_mst(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); +} + #endif -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 15/20] drm/i915/dp: Extract drm_dp_has_sink_count()
Since other drivers are also going to need to be aware of the sink count in order to do proper dongle detection, we might as well steal i915's DP_SINK_COUNT helpers and move them into DRM helpers so that other dirvers can use them as well. Note that this also starts using intel_dp_has_sink_count() in intel_dp_detect_dpcd(), which is a functional change. Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 21 - include/drm/drm_dp_helper.h | 8 +++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4f845995f1f66..863e0babc1903 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -714,6 +714,28 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_dp_set_subconnector_property); +/** + * drm_dp_has_sink_count() - 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 + * + * Returns: %True if the (e)DP connector has a valid sink count that should + * be probed, %false otherwise. + */ +bool drm_dp_has_sink_count(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_has_sink_count); + /* * I2C-over-AUX implementation */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 984e49194ca31..35a4779a442e2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4634,6 +4634,16 @@ 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_has_sink_count(&intel_dp->attached_connector->base, +intel_dp->dpcd, +&intel_dp->desc); +} static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) @@ -4653,13 +4663,7 @@ 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)) { + if (intel_dp_has_sink_count(intel_dp)) { u8 count; ssize_t r; @@ -5939,9 +5943,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/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 1349f16564ace..a1413a531eaf4 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1631,6 +1631,11 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector, const u8 *dpcd, const u8 port_cap[4]); +struct drm_dp_desc; +bool drm_dp_has_sink_count(struct drm_connector *connector, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const struct drm_dp_desc *desc); + void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); @@ -1689,7 +1694,8 @@ enum drm_dp_quirk { * @DP_DPCD_QUIRK_NO_SINK_COUNT: * * The device does not set SINK_COUNT to a non-zero value. -* The driver should ignore SINK_COUNT during detection. +* The driver should ignore SINK_COUNT during detection. Note that +
[Intel-gfx] [RFC v4 12/20] drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths
Currently we perform both short IRQ handling for DP, and connector reprobing in the HPD IRQ handler. However since we need to grab connection_mutex in order to reprobe a connector, in theory we could accidentally block ourselves from handling any short IRQs until after a modeset completes if a connector hotplug happens to occur in parallel with a modeset. I haven't seen this actually happen yet, but since we're cleaning up nouveau's hotplug handling code anyway and we already have a hpd worker, we can simply fix this by only relying on the HPD worker to actually reprobe connectors when we receive a HPD IRQ. We also add a mask to nouveau_drm to keep track of which connectors are waiting to be reprobed in response to an HPD IRQ. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 42 + drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 70 ++--- drivers/gpu/drm/nouveau/nouveau_display.h | 1 + drivers/gpu/drm/nouveau/nouveau_dp.c| 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + 7 files changed, 86 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4a29f691c08e4..637e91594fbe8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1138,6 +1138,20 @@ 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) { @@ -1147,8 +1161,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - int ret; bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { @@ -1156,31 +1168,9 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - } - - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); - - 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 d6de5cb8e2238..d0b859c4a80ea 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 13016769a194b..bceb48a2dfca6 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[] = { }
[Intel-gfx] [RFC v4 13/20] drm/i915/dp: Extract drm_dp_downstream_read_info()
We're going to be doing the same probing process in nouveau for determining downstream DP port capabilities, so let's deduplicate the work by moving i915's code for handling this into a shared helper: drm_dp_downstream_read_info(). Note that when we do this, we also do make some functional changes while we're at it: * We always clear the downstream port info before trying to read it, just to make things easier for the caller * We skip reading downstream port info if the DPCD indicates that we don't support downstream port info * We only read as many bytes as needed for the reported number of downstream ports, no sense in reading the whole thing every time v2: * Fixup logic for calculating the downstream port length to account for the fact that downstream port caps can be either 1 byte or 4 bytes long. We can actually skip fixing the max_clock/max_bpc helpers here since they all check for DP_DETAILED_CAP_INFO_AVAILABLE anyway. * Fix ret code check for drm_dp_dpcd_read Reviewed-by: Sean Paul Signed-off-by: Lyude Paul --- drivers/gpu/drm/drm_dp_helper.c | 46 + drivers/gpu/drm/i915/display/intel_dp.c | 14 ++-- include/drm/drm_dp_helper.h | 3 ++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4c21cf69dad5a..4f845995f1f66 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -423,6 +423,52 @@ 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; +} + +/** + * drm_dp_downstream_read_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 + * + * 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_downstream_read_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_downstream_read_info); + /** * drm_dp_downstream_max_clock() - extract branch device max * pixel rate for legacy VGA diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1e29d3a012856..984e49194ca31 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4685,18 +4685,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; + return drm_dp_downstream_read_info(&intel_dp->aux, intel_dp->dpcd, + intel_dp->downstream_ports) == 0; } static bool diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 5c28199248626..1349f16564ace 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1613,6 +1613,9 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, u8 real_edid_checksum); +int drm_dp_downstream_read_info(struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER
[Intel-gfx] [RFC v4 05/20] drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new()
Since fa3cdf8d0b09 ("drm/nouveau: Reset MST branching unit before enabling") we've been clearing DP_MST_CTRL before we start enabling MST. Since then clearing DP_MST_CTRL in nv50_mstm_new() has been unnecessary and redundant, so let's remove it. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index e7874877da858..c4d138f0ca054 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1535,17 +1535,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; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 08/20] drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling
First some backstory here: Currently, we keep track of whether or not we've enabled MST or not by trying to piggy-back off the MST helpers. This means that in order to check whether MST is enabled or not, we actually need to grab drm_dp_mst_topology_mgr.lock. Back when I originally wrote this, I did this piggy-backing with the intention that I'd eventually be teaching our MST helpers how to recover when an MST device has stopped responding, which in turn would require the MST helpers having a way of disabling MST independently of the driver. Note that this was before I reworked locking in the MST helpers, so at the time we were sticking random things under &mgr->lock - which grabbing this lock was meant to protect against. This never came to fruition because doing such a reset safely turned out to be a lot more painful and impossible then it sounds, and also just risks us working around issues with our MST handlers that should be properly fixed instead. Even if it did though, simply calling drm_dp_mst_topology_mgr_set_mst() from the MST helpers (with the exception of when we're tearing down our MST managers, that's always OK) wouldn't have been a bad idea, since drivers like nouveau and i915 need to do their own book keeping immediately after disabling MST. So-implementing that would likely require adding a hook for helper-triggered MST disables anyway. So, fast forward to now - we want to start adding support for all of the miscellaneous bits of the DP protocol (for both SST and MST) we're missing before moving on to supporting more complicated features like supporting different BPP values on MST, DSC, etc. Since many of these features only exist on SST and make use of DP HPD IRQs, we want to be able to atomically check whether we're servicing an MST IRQ or SST IRQ in nouveau_connector_hotplug(). Currently we literally don't do this at all, and just handle any kind of possible DP IRQ we could get including ESIs - even if MST isn't actually enabled. This would be very complicated and difficult to fix if we need to hold &mgr->lock while handling SST IRQs to ensure that the MST topology state doesn't change under us. What we really want here is to do our own tracking of whether MST is enabled or not, similar to drivers like i915, and define our own locking order to decomplicate things and avoid hitting locking issues in the future. So, let's do this by refactoring our MST probing/enabling code to use our own MST bookkeeping, along with adding a lock for protecting DP state that needs to be checked outside of our connector probing functions. While we're at it, we also remove a bunch of unneeded steps we perform when probing/enabling MST: * Enabling bits in MSTM_CTRL before calling drm_dp_mst_topology_mgr_set_mst(). I don't think these ever actually did anything, since the nvif methods for enabling MST don't actually do anything DPCD related and merely indicate to nvkm that we've turned on MST. * Checking the MSTM_CTRL bit is intact when checking the state of an enabled MST topology in nv50_mstm_detect(). I just added this to be safe originally, but now that we try reading the DPCD when probing DP connectors it shouldn't be needed as that will abort our hotplug probing if the device was removed well before we start checking for MST.. * All of the duplicate DPCD version checks. This leaves us with much nicer looking code, a much more sensible locking scheme, and an easy way of checking whether MST is enabled or not for handling DP HPD IRQs. v2: * Get rid of accidental newlines v4: * Fix uninitialized usage of mstm in nv50_mstm_detect() - thanks kernel bot! Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/disp.c | 6 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 192 +--- drivers/gpu/drm/nouveau/nouveau_connector.c | 14 +- drivers/gpu/drm/nouveau/nouveau_display.c | 2 +- drivers/gpu/drm/nouveau/nouveau_display.h | 2 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 132 -- drivers/gpu/drm/nouveau/nouveau_encoder.h | 33 +++- 7 files changed, 244 insertions(+), 137 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 3f046b917c85c..3ee836dc5058f 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -54,8 +54,9 @@ nv04_encoder_get_connector(struct nouveau_encoder *encoder) } 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; @@ -67,6 +68,9 @@ nv04_display_fini(struct drm_device *dev, bool suspend) if (nv_two_heads(dev)) NVWriteCRTC(dev, 1, N
[Intel-gfx] [RFC v4 06/20] drm/nouveau/kms: Search for encoders' connectors properly
While the way we find the associated connector for an encoder is just fine for legacy modesetting, it's not correct for nv50+ since that uses atomic modesetting. For reference, see the drm_encoder kdocs. Fix this by removing nouveau_encoder_connector_get(), and replacing it with nv04_encoder_get_connector(), nv50_outp_get_old_connector(), and nv50_outp_get_new_connector(). v2: * Don't line-wrap for_each_(old|new)_connector_in_state in nv50_outp_get_(old|new)_connector() - sravn v3: * Fix potential uninitialized usage of nv_connector (needs to be initialized to NULL at the start). Thanks kernel test robot! v4: * Actually fix uninitialized nv_connector usage in nv50_audio_component_get_eld(). The previous fix wouldn't have worked since we would have started out with nv_connector == NULL, but wouldn't clear it after a single drm_for_each_encoder() iteration. Thanks again Kernel bot! Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 18 + drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 88 + drivers/gpu/drm/nouveau/nouveau_connector.c | 14 drivers/gpu/drm/nouveau/nouveau_encoder.h | 6 +- 9 files changed, 105 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c index ffdd447d87068..22d10f3285597 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 f9f4482c79b54..42687ea2a4ca3 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 900ab69df7e8f..3f046b917c85c 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -35,6 +35,24 @@ #include +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) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/
[Intel-gfx] [RFC v4 07/20] drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable()
Just use drm_dp_dpcd_(readb|writeb)() so we get automatic DPCD logging Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 11 +++ 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 255a281249bc8..612d98fa0a2dc 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1630,19 +1630,22 @@ nv50_sor_disable(struct drm_encoder *encoder, { 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); } } -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 11/20] drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect()
For whatever reason we currently unset the EDID for DP CEC support when responding to the connector being unplugged, instead of just doing it in nouveau_connector_detect() where we set the CEC EDID. This isn't really needed and could even potentially cause us to forget to unset the EDID if the connector is removed without a corresponding hpd event, so let's fix that. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index b90591114faaf..4a29f691c08e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -633,10 +633,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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); @@ -1174,8 +1175,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) return NVIF_NOTIFY_DROP; } - if (!plugged) - drm_dp_cec_unset_edid(&nv_connector->aux); NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); drm_helper_hpd_irq_event(connector->dev); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 16/20] drm/i915/dp: Extract drm_dp_get_sink_count()
And of course, we'll also need to read the sink count from other drivers as well if we're checking whether or not it's supported. So, let's extract the code for this into another helper. v2: * Fix drm_dp_dpcd_readb() ret check * Add back comment and move back sink_count assignment in intel_dp_get_dpcd() Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 11 +-- include/drm/drm_dp_helper.h | 1 + 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 863e0babc1903..67ad05eb05b7e 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -736,6 +736,28 @@ bool drm_dp_has_sink_count(struct drm_connector *connector, } EXPORT_SYMBOL(drm_dp_has_sink_count); +/** + * drm_dp_get_sink_count() - Retrieve the sink count for a given sink + * @aux: The DP AUX channel to use + * + * Returns: The current sink count reported by @aux, or a negative error code + * otherwise. + */ +int drm_dp_get_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_get_sink_count); + /* * I2C-over-AUX implementation */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 35a4779a442e2..4337321a3be4f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4648,6 +4648,8 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + int ret; + if (!intel_dp_read_dpcd(intel_dp)) return false; @@ -4664,11 +4666,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } if (intel_dp_has_sink_count(intel_dp)) { - u8 count; - ssize_t r; - - r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count); - if (r < 1) + ret = drm_dp_get_sink_count(&intel_dp->aux); + if (ret < 0) return false; /* @@ -4676,7 +4675,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 diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index a1413a531eaf4..0c141fc81aaa8 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1635,6 +1635,7 @@ struct drm_dp_desc; bool drm_dp_has_sink_count(struct drm_connector *connector, const u8 dpcd[DP_RECEIVER_CAP_SIZE], const struct drm_dp_desc *desc); +int drm_dp_get_sink_count(struct drm_dp_aux *aux); void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 19/20] drm/i915/dp: Extract drm_dp_read_dpcd_caps()
Since DP 1.3, it's been possible for DP receivers to specify an additional set of DPCD capabilities, which can take precedence over the capabilities reported at DP_DPCD_REV. Basically any device supporting DP is going to need to read these in an identical manner, in particular nouveau, so let's go ahead and just move this code out of i915 into a shared DRM DP helper that we can use in other drivers. v2: * Remove redundant dpcd[DP_DPCD_REV] == 0 check * Fix drm_dp_dpcd_read() ret checks Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 77 + drivers/gpu/drm/i915/display/intel_dp.c | 60 +--- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- include/drm/drm_dp_helper.h | 3 + 5 files changed, 83 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 67ad05eb05b7e..9c99d21b42c15 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -433,6 +433,83 @@ static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 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 h could be at a value less than +* the true capability of the panel. The only way to check is to +* then compare h 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_downstream_read_info() - read DPCD downstream port info if available * @aux: DisplayPort AUX channel diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4337321a3be4f..fb7872e2a0b93 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 h could be at a value less than -* the true capability of the panel. The only way to check is to -* then compare h 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, -
[Intel-gfx] [RFC v4 10/20] drm/nouveau/kms: Use new drm_dp_has_mst() helper for checking MST caps
Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 16 +++- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 032afc73e2a33..201c0b4335563 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -44,7 +44,6 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, struct nv50_mstm *mstm = NULL; int ret; u8 *dpcd = outp->dp.dpcd; - u8 tmp; ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); if (ret == DP_RECEIVER_CAP_SIZE && dpcd[DP_DPCD_REV]) { @@ -56,19 +55,10 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, return connector_status_disconnected; } - if (nouveau_mst) + if (nouveau_mst) { mstm = outp->dp.mstm; - - if (mstm) { - if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_12) { - ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &tmp); - if (ret < 0) - return connector_status_disconnected; - - mstm->can_mst = !!(tmp & DP_MST_CAP); - } else { - mstm->can_mst = false; - } + if (mstm) + mstm->can_mst = drm_dp_has_mst(aux, dpcd); } return connector_status_connected; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 20/20] drm/nouveau/kms: Start using drm_dp_read_dpcd_caps()
Now that we've extracted i915's code for reading both the normal DPCD caps and extended DPCD caps into a shared helper, let's start using this in nouveau to enable us to start checking extended DPCD caps for free. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 14 ++ 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 89afc97ee2591..271a0a863a0e1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -55,15 +55,13 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, int ret; u8 *dpcd = outp->dp.dpcd; - ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); - if (ret == DP_RECEIVER_CAP_SIZE && dpcd[DP_DPCD_REV]) { - ret = drm_dp_read_desc(aux, &outp->dp.desc, - drm_dp_is_branch(dpcd)); - if (ret < 0) - goto out; - } else { + 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; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [RFC v4 18/20] drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Currently in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds(), we start the connector probing process by releasing the previous EDID and informing DRM of the change. However, since commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector") drm_connector_update_edid_property() actually checks whether the new EDID we've specified is different from the previous one, and updates the connector's epoch accordingly if it is. But, because we always set the EDID to NULL first in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds() we end up making DRM think that the EDID changes every single time we do a connector probe - which isn't needed. So, let's fix this by not clearing the EDID at the start of the connector probing process, and instead simply changing or removing it once near the end of the probing process. This will help prevent us from sending unneeded hotplug events to userspace when nothing has actually changed. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 54 ++--- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 637e91594fbe8..49dd0cbc332ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -528,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) { @@ -541,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 @@ -560,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); @@ -609,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); @@ -652,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_connec
Re: [Intel-gfx] [RFC v4 16/20] drm/i915/dp: Extract drm_dp_get_sink_count()
On Wed, 2020-08-26 at 10:05 +0300, Jani Nikula wrote: > On Tue, 25 Aug 2020, Lyude Paul wrote: > > And of course, we'll also need to read the sink count from other drivers > > as well if we're checking whether or not it's supported. So, let's > > extract the code for this into another helper. > > > > v2: > > * Fix drm_dp_dpcd_readb() ret check > > * Add back comment and move back sink_count assignment in > > intel_dp_get_dpcd() > > > > Signed-off-by: Lyude Paul > > Reviewed-by: Sean Paul > > --- > > drivers/gpu/drm/drm_dp_helper.c | 22 ++ > > drivers/gpu/drm/i915/display/intel_dp.c | 11 +-- > > include/drm/drm_dp_helper.h | 1 + > > 3 files changed, 28 insertions(+), 6 deletions(-) > > > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > > b/drivers/gpu/drm/drm_dp_helper.c > > index 863e0babc1903..67ad05eb05b7e 100644 > > --- a/drivers/gpu/drm/drm_dp_helper.c > > +++ b/drivers/gpu/drm/drm_dp_helper.c > > @@ -736,6 +736,28 @@ bool drm_dp_has_sink_count(struct drm_connector > > *connector, > > } > > EXPORT_SYMBOL(drm_dp_has_sink_count); > > > > +/** > > + * drm_dp_get_sink_count() - Retrieve the sink count for a given sink > > From the department of bikeshedding... > > Should we have a naming scheme where it's obvious whether a function > will do DPCD access, or just shuffle existing data? > > For example, drm_dp_read_foo() for anything with DPCD access > vs. drm_dp_get_foo() or even simpler for anything that only processes > pre-read data? > > > + * @aux: The DP AUX channel to use > > + * > > + * Returns: The current sink count reported by @aux, or a negative error > > code > > + * otherwise. > > + */ > > +int drm_dp_get_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; > > Makes me wonder if that shouldn't be at drm_dp_dpcd_read() level, for > reads returning 0..len-1 bytes. Not necessarily part of this series, but > seems silly to set a precedent to start handling that return value all > over the place. > Yeah definitely - I'm probably going to keep this code here for now, but I would like to convert drm_dp_dpcd_readb/writeb() to just return 0 on success (all bytes written, I've never once seen a situation where we got less bytes than we read - it's always all or nothing) and negative error code on failure. I'll get to that soon > BR, > Jani. > > > + > > + return DP_GET_SINK_COUNT(count); > > +} > > +EXPORT_SYMBOL(drm_dp_get_sink_count); > > + > > /* > > * I2C-over-AUX implementation > > */ > > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > > b/drivers/gpu/drm/i915/display/intel_dp.c > > index 35a4779a442e2..4337321a3be4f 100644 > > --- a/drivers/gpu/drm/i915/display/intel_dp.c > > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > > @@ -4648,6 +4648,8 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) > > static bool > > intel_dp_get_dpcd(struct intel_dp *intel_dp) > > { > > + int ret; > > + > > if (!intel_dp_read_dpcd(intel_dp)) > > return false; > > > > @@ -4664,11 +4666,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) > > } > > > > if (intel_dp_has_sink_count(intel_dp)) { > > - u8 count; > > - ssize_t r; > > - > > - r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count); > > - if (r < 1) > > + ret = drm_dp_get_sink_count(&intel_dp->aux); > > + if (ret < 0) > > return false; > > > > /* > > @@ -4676,7 +4675,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 > > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > > index a1413a531eaf4..0c141fc81aaa8 100644 > > --- a/include/drm/drm_dp_helper.h > > +++ b/include/drm/drm_dp_helper.h > > @@ -1635,6 +1635,7 @@ struct drm_dp_desc; > > bool drm_dp_has_sink_count(struct drm_connector *connector, > >const u8 dpcd[DP_RECEIVER_CAP_SIZE], > >const struct drm_dp_desc *desc); > > +int drm_dp_get_sink_count(struct drm_dp_aux *aux); > > > > void drm_dp_remote_aux_init(struct drm_dp_aux *aux); > > void drm_dp_aux_init(struct drm_dp_aux *aux); -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [RFC v4 16/20] drm/i915/dp: Extract drm_dp_get_sink_count()
On Wed, 2020-08-26 at 10:05 +0300, Jani Nikula wrote: > On Tue, 25 Aug 2020, Lyude Paul wrote: > > And of course, we'll also need to read the sink count from other drivers > > as well if we're checking whether or not it's supported. So, let's > > extract the code for this into another helper. > > > > v2: > > * Fix drm_dp_dpcd_readb() ret check > > * Add back comment and move back sink_count assignment in > > intel_dp_get_dpcd() > > > > Signed-off-by: Lyude Paul > > Reviewed-by: Sean Paul > > --- > > drivers/gpu/drm/drm_dp_helper.c | 22 ++ > > drivers/gpu/drm/i915/display/intel_dp.c | 11 +-- > > include/drm/drm_dp_helper.h | 1 + > > 3 files changed, 28 insertions(+), 6 deletions(-) > > > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > > b/drivers/gpu/drm/drm_dp_helper.c > > index 863e0babc1903..67ad05eb05b7e 100644 > > --- a/drivers/gpu/drm/drm_dp_helper.c > > +++ b/drivers/gpu/drm/drm_dp_helper.c > > @@ -736,6 +736,28 @@ bool drm_dp_has_sink_count(struct drm_connector > > *connector, > > } > > EXPORT_SYMBOL(drm_dp_has_sink_count); > > > > +/** > > + * drm_dp_get_sink_count() - Retrieve the sink count for a given sink > > From the department of bikeshedding... > > Should we have a naming scheme where it's obvious whether a function > will do DPCD access, or just shuffle existing data? > > For example, drm_dp_read_foo() for anything with DPCD access > vs. drm_dp_get_foo() or even simpler for anything that only processes > pre-read data? Forgot to address this comment - yeah, I think that would be a good idea. I'll go through my previous patches and make sure that they match this naming scheme as well. > > > + * @aux: The DP AUX channel to use > > + * > > + * Returns: The current sink count reported by @aux, or a negative error > > code > > + * otherwise. > > + */ > > +int drm_dp_get_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; > > Makes me wonder if that shouldn't be at drm_dp_dpcd_read() level, for > reads returning 0..len-1 bytes. Not necessarily part of this series, but > seems silly to set a precedent to start handling that return value all > over the place. > > BR, > Jani. > > > + > > + return DP_GET_SINK_COUNT(count); > > +} > > +EXPORT_SYMBOL(drm_dp_get_sink_count); > > + > > /* > > * I2C-over-AUX implementation > > */ > > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > > b/drivers/gpu/drm/i915/display/intel_dp.c > > index 35a4779a442e2..4337321a3be4f 100644 > > --- a/drivers/gpu/drm/i915/display/intel_dp.c > > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > > @@ -4648,6 +4648,8 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) > > static bool > > intel_dp_get_dpcd(struct intel_dp *intel_dp) > > { > > + int ret; > > + > > if (!intel_dp_read_dpcd(intel_dp)) > > return false; > > > > @@ -4664,11 +4666,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) > > } > > > > if (intel_dp_has_sink_count(intel_dp)) { > > - u8 count; > > - ssize_t r; > > - > > - r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count); > > - if (r < 1) > > + ret = drm_dp_get_sink_count(&intel_dp->aux); > > + if (ret < 0) > > return false; > > > > /* > > @@ -4676,7 +4675,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 > > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > > index a1413a531eaf4..0c141fc81aaa8 100644 > > --- a/include/drm/drm_dp_helper.h > > +++ b/include/drm/drm_dp_helper.h > > @@ -1635,6 +1635,7 @@ struct drm_dp_desc; > > bool drm_dp_has_sink_count(struct drm_connector *connector, > >const u8 dpcd[DP_RECEIVER_CAP_SIZE], > >const struct drm_dp_desc *desc); > > +int drm_dp_get_sink_count(struct drm_dp_aux *aux); > > > > void drm_dp_remote_aux_init(struct drm_dp_aux *aux); > > void drm_dp_aux_init(struct drm_dp_aux *aux); -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 05/20] drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new()
Since fa3cdf8d0b09 ("drm/nouveau: Reset MST branching unit before enabling") we've been clearing DP_MST_CTRL before we start enabling MST. Since then clearing DP_MST_CTRL in nv50_mstm_new() has been unnecessary and redundant, so let's remove it. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index e7874877da858..c4d138f0ca054 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1535,17 +1535,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; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 01/20] drm/nouveau/kms: Fix some indenting in nouveau_dp_detect()
Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8a0f7994e1aeb..ee778ddc95fae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -76,10 +76,10 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) nv_encoder->dp.link_nr = dpcd[2] & 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[0]); 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,7 +87,7 @@ 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); nouveau_dp_probe_oui(dev, aux, dpcd); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 00/20] drm/dp, i915, nouveau: Cleanup nouveau HPD and add DP features from i915
Most of the reason I'm asking for an RFC here is because this code pulls a lot of code out of i915 and into shared DP helpers. Anyway-nouveau's HPD related code has been collecting dust for a while. Other then the occasional runtime PM related and MST related fixes, we're missing a lot of nice things that have been added to DRM since this was originally written. Additionally, the code is just really unoptimized in general: * We handle connector probing in the same context that we handle short IRQs in for DP, which means connector probing could potentially block ESI handling for MST * When we receive a hotplug event from a connector, we reprobe every single connector instead of just the connector that was hotplugged * Additionally because of the above reason, combined with the fact I had the bad idea of reusing some of the MST locks when I last rewrote nouveau's DP MST detection, trying to handle any other events that require short HPD IRQs is a bit awkward to actually implement. * We don't actually properly check whether EDIDs change or not when reprobing, which means we basically send out a hotplug event every single time we receive one even if nothing has changed Additionally, the code for handling DP that we have in nouveau is also quite unoptimized in general, doesn't use a lot of helpers that have been added since it was written, and is also missing quite a number of features: * Downstream port capability probing * Extended DPRX cap parsing * SINK_COUNT handling for hpd on dongles Luckily for us - all of these are implemented in i915 already. Since there's no reason for us to reinvent the wheel, and having more shared code is always nice, I decided to take the opportunity to extract the code for all of these features from i915 into a set of core DP helpers, which both i915 and nouveau (and hopefully other drivers in the future) can use. As well, this patch series also addesses the other general connector probing related issues I mentioned above, along with rewriting how we handle MST probing so we don't hit any surprise locking design issues in the future. As a note - most of this work is motivated by the fact that I'm planning on adding max_bpc/output_bpc prop support, DSC support (for both MST and SST, along with proper helpers for handling bandwidth limitations and DSC), and fallback link retraining. I figured I might as clean this code up and implement missing DP features like the ones mentioned here before moving on to those tasks. Lyude Paul (20): drm/nouveau/kms: Fix some indenting in nouveau_dp_detect() drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc() drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new() drm/nouveau/kms: Search for encoders' connectors properly drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable() drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling drm/i915/dp: Extract drm_dp_read_mst_cap() drm/nouveau/kms: Use new drm_dp_read_mst_cap() helper for checking MST caps drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect() drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths drm/i915/dp: Extract drm_dp_read_downstream_info() drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation drm/i915/dp: Extract drm_dp_read_sink_count_cap() drm/i915/dp: Extract drm_dp_read_sink_count() drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT drm/nouveau/kms: Don't change EDID when it hasn't actually changed drm/i915/dp: Extract drm_dp_read_dpcd_caps() drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() drivers/gpu/drm/drm_dp_helper.c | 187 +++- drivers/gpu/drm/drm_dp_mst_topology.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 124 ++-- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 24 +- drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 305 +++- drivers/gpu/drm/nouveau/nouveau_connector.c | 132 - drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 72 - drivers/gpu/drm/nouveau/nouveau_display.h | 3 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 210 +++--- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_encoder.h | 48 ++-
[Intel-gfx] [PATCH v5 06/20] drm/nouveau/kms: Search for encoders' connectors properly
While the way we find the associated connector for an encoder is just fine for legacy modesetting, it's not correct for nv50+ since that uses atomic modesetting. For reference, see the drm_encoder kdocs. Fix this by removing nouveau_encoder_connector_get(), and replacing it with nv04_encoder_get_connector(), nv50_outp_get_old_connector(), and nv50_outp_get_new_connector(). v2: * Don't line-wrap for_each_(old|new)_connector_in_state in nv50_outp_get_(old|new)_connector() - sravn v3: * Fix potential uninitialized usage of nv_connector (needs to be initialized to NULL at the start). Thanks kernel test robot! v4: * Actually fix uninitialized nv_connector usage in nv50_audio_component_get_eld(). The previous fix wouldn't have worked since we would have started out with nv_connector == NULL, but wouldn't clear it after a single drm_for_each_encoder() iteration. Thanks again Kernel bot! Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 18 + drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 88 + drivers/gpu/drm/nouveau/nouveau_connector.c | 14 drivers/gpu/drm/nouveau/nouveau_encoder.h | 6 +- 9 files changed, 105 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c index ffdd447d87068..22d10f3285597 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 f9f4482c79b54..42687ea2a4ca3 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 900ab69df7e8f..3f046b917c85c 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -35,6 +35,24 @@ #include +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) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/
[Intel-gfx] [PATCH v5 02/20] drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc()
Noticed this while going through our DP code - we use an open-coded version of drm_dp_read_desc() instead of just using the helper, so change that. This will also let us use quirks in the future if we end up needing them. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_dp.c| 30 +++-- drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 7674025a4bfe8..e12957e6faa7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -435,7 +435,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) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ee778ddc95fae..c4e9c21d4dd2b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,27 +36,9 @@ 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) -{ - 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]); - -} - 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); @@ -89,7 +71,13 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); - nouveau_dp_probe_oui(dev, aux, dpcd); + ret = drm_dp_read_desc(&nv_connector->aux, &nv_encoder->dp.desc, + drm_dp_is_branch(dpcd)); + if (ret) { + NV_ERROR(drm, "Failed to read DP descriptor on %s: %d\n", +nv_connector->base.name, ret); + return ret; + } ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst); if (ret == 1) diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index a72c412ac8b14..6424cdcb4913f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -33,6 +33,7 @@ #include #include "dispnv04/disp.h" struct nv50_head_atom; +struct nouveau_connector; #define NV_DPMS_CLEARED 0x80 @@ -64,6 +65,7 @@ struct nouveau_encoder { struct nv50_mstm *mstm; int link_nr; int link_bw; + struct drm_dp_desc desc; } dp; }; @@ -104,7 +106,7 @@ enum nouveau_dp_status { NOUVEAU_DP_MST, }; -int nouveau_dp_detect(struct nouveau_encoder *); +int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, struct nouveau_encoder *, const struct drm_display_mode *, -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 03/20] drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c
Since this actually logs accesses, we should probably always be using this imho… Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c4e9c21d4dd2b..8db9216d52c69 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -42,16 +42,12 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, { struct drm_device *dev = nv_encoder->base.base.dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c_aux *aux; - u8 dpcd[8]; + struct drm_dp_aux *aux = &nv_connector->aux; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; int ret; - aux = nv_encoder->aux; - if (!aux) - return -ENODEV; - - ret = nvkm_rdaux(aux, DP_DPCD_REV, dpcd, sizeof(dpcd)); - if (ret) + ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); + if (ret != sizeof(dpcd)) return ret; nv_encoder->dp.link_bw = 27000 * dpcd[1]; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 15/20] drm/i915/dp: Extract drm_dp_read_sink_count_cap()
Since other drivers are also going to need to be aware of the sink count in order to do proper dongle detection, we might as well steal i915's DP_SINK_COUNT helpers and move them into DRM helpers so that other dirvers can use them as well. Note that this also starts using intel_dp_has_sink_count() in intel_dp_detect_dpcd(), which is a functional change. v5: * Change name from drm_dp_has_sink_count() to drm_dp_read_sink_count_cap() Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 21 - include/drm/drm_dp_helper.h | 8 +++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index f3643894ad951..65ff21ae0c271 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -726,6 +726,28 @@ 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 + * + * 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); + /* * I2C-over-AUX implementation */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 9c4b806af8c79..38318ae935f88 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4634,6 +4634,16 @@ 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) @@ -4653,13 +4663,7 @@ 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)) { + if (intel_dp_has_sink_count(intel_dp)) { u8 count; ssize_t r; @@ -5939,9 +5943,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/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index b8716b200666f..4c56ce4dc54fc 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1631,6 +1631,11 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector, const u8 *dpcd, const u8 port_cap[4]); +struct drm_dp_desc; +bool drm_dp_read_sink_count_cap(struct drm_connector *connector, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const struct drm_dp_desc *desc); + void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); @@ -1689,7 +1694,8 @@ enum drm_dp_quirk { * @DP_DPCD_QUIRK_NO_SINK_COUNT: * * The device does not set SINK_COUNT to a non-zero value. -* The driver should ignore SINK_CO
[Intel-gfx] [PATCH v5 10/20] drm/nouveau/kms: Use new drm_dp_read_mst_cap() helper for checking MST caps
Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 16 +++- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 032afc73e2a33..a5934064a75ea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -44,7 +44,6 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, struct nv50_mstm *mstm = NULL; int ret; u8 *dpcd = outp->dp.dpcd; - u8 tmp; ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); if (ret == DP_RECEIVER_CAP_SIZE && dpcd[DP_DPCD_REV]) { @@ -56,19 +55,10 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, return connector_status_disconnected; } - if (nouveau_mst) + if (nouveau_mst) { mstm = outp->dp.mstm; - - if (mstm) { - if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_12) { - ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &tmp); - if (ret < 0) - return connector_status_disconnected; - - mstm->can_mst = !!(tmp & DP_MST_CAP); - } else { - mstm->can_mst = false; - } + if (mstm) + mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd); } return connector_status_connected; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 08/20] drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling
First some backstory here: Currently, we keep track of whether or not we've enabled MST or not by trying to piggy-back off the MST helpers. This means that in order to check whether MST is enabled or not, we actually need to grab drm_dp_mst_topology_mgr.lock. Back when I originally wrote this, I did this piggy-backing with the intention that I'd eventually be teaching our MST helpers how to recover when an MST device has stopped responding, which in turn would require the MST helpers having a way of disabling MST independently of the driver. Note that this was before I reworked locking in the MST helpers, so at the time we were sticking random things under &mgr->lock - which grabbing this lock was meant to protect against. This never came to fruition because doing such a reset safely turned out to be a lot more painful and impossible then it sounds, and also just risks us working around issues with our MST handlers that should be properly fixed instead. Even if it did though, simply calling drm_dp_mst_topology_mgr_set_mst() from the MST helpers (with the exception of when we're tearing down our MST managers, that's always OK) wouldn't have been a bad idea, since drivers like nouveau and i915 need to do their own book keeping immediately after disabling MST. So-implementing that would likely require adding a hook for helper-triggered MST disables anyway. So, fast forward to now - we want to start adding support for all of the miscellaneous bits of the DP protocol (for both SST and MST) we're missing before moving on to supporting more complicated features like supporting different BPP values on MST, DSC, etc. Since many of these features only exist on SST and make use of DP HPD IRQs, we want to be able to atomically check whether we're servicing an MST IRQ or SST IRQ in nouveau_connector_hotplug(). Currently we literally don't do this at all, and just handle any kind of possible DP IRQ we could get including ESIs - even if MST isn't actually enabled. This would be very complicated and difficult to fix if we need to hold &mgr->lock while handling SST IRQs to ensure that the MST topology state doesn't change under us. What we really want here is to do our own tracking of whether MST is enabled or not, similar to drivers like i915, and define our own locking order to decomplicate things and avoid hitting locking issues in the future. So, let's do this by refactoring our MST probing/enabling code to use our own MST bookkeeping, along with adding a lock for protecting DP state that needs to be checked outside of our connector probing functions. While we're at it, we also remove a bunch of unneeded steps we perform when probing/enabling MST: * Enabling bits in MSTM_CTRL before calling drm_dp_mst_topology_mgr_set_mst(). I don't think these ever actually did anything, since the nvif methods for enabling MST don't actually do anything DPCD related and merely indicate to nvkm that we've turned on MST. * Checking the MSTM_CTRL bit is intact when checking the state of an enabled MST topology in nv50_mstm_detect(). I just added this to be safe originally, but now that we try reading the DPCD when probing DP connectors it shouldn't be needed as that will abort our hotplug probing if the device was removed well before we start checking for MST.. * All of the duplicate DPCD version checks. This leaves us with much nicer looking code, a much more sensible locking scheme, and an easy way of checking whether MST is enabled or not for handling DP HPD IRQs. v2: * Get rid of accidental newlines v4: * Fix uninitialized usage of mstm in nv50_mstm_detect() - thanks kernel bot! Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv04/disp.c | 6 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 192 +--- drivers/gpu/drm/nouveau/nouveau_connector.c | 14 +- drivers/gpu/drm/nouveau/nouveau_display.c | 2 +- drivers/gpu/drm/nouveau/nouveau_display.h | 2 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 132 -- drivers/gpu/drm/nouveau/nouveau_encoder.h | 33 +++- 7 files changed, 244 insertions(+), 137 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 3f046b917c85c..3ee836dc5058f 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -54,8 +54,9 @@ nv04_encoder_get_connector(struct nouveau_encoder *encoder) } 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; @@ -67,6 +68,9 @@ nv04_display_fini(struct drm_device *dev, bool suspend) if (nv_two_heads(dev)) NVWriteCRTC(dev, 1, N
[Intel-gfx] [PATCH v5 09/20] drm/i915/dp: Extract drm_dp_read_mst_cap()
Just a tiny drive-by cleanup, we can consolidate i915's code for checking for MST support into a helper to be shared across drivers. v5: * Drop !!() * Move drm_dp_has_mst() out of header * Change name from drm_dp_has_mst() to drm_dp_read_mst_cap() Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_mst_topology.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 18 ++ include/drm/drm_dp_mst_helper.h | 3 +-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 67dd72ea200e0..17dbed0a9800d 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3486,6 +3486,28 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count) return dp_link_bw * dp_link_count / 2; } +/** + * 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 diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 79c27f91f42c0..4c7314b7a84e4 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4699,20 +4699,6 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) 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; -} - static bool intel_dp_can_mst(struct intel_dp *intel_dp) { @@ -4720,7 +4706,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 +4715,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", diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 8b9eb4db3381c..6ae5860d8644e 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -728,10 +728,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); - +bool drm_dp_read_mst_cap(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state); - int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 14/20] drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation
This adds support for querying the maximum clock rate of a downstream port on a DisplayPort connection. Generally, downstream ports refer to active dongles which can have their own pixel clock limits. Note as well, we also start marking the connector as disconnected if we can't read the DPCD, since we wouldn't be able to do anything without DPCD access anyway. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 3 +++ drivers/gpu/drm/nouveau/nouveau_dp.c | 15 +++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 8e1effb10425d..d2141ca16107b 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1258,7 +1258,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; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 005750aeb6d4f..ad852e572cfec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -61,6 +61,11 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd); } + ret = drm_dp_read_downstream_info(aux, dpcd, + outp->dp.downstream_ports); + if (ret < 0) + return connector_status_disconnected; + return connector_status_connected; } @@ -176,8 +181,6 @@ void nouveau_dp_irq(struct nouveau_drm *drm, /* 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) */ @@ -188,15 +191,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_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index eef4643f5f982..c1924a4529a7b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -72,6 +72,7 @@ struct nouveau_encoder { struct mutex hpd_irq_lock; u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; } dp; }; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 19/20] drm/i915/dp: Extract drm_dp_read_dpcd_caps()
Since DP 1.3, it's been possible for DP receivers to specify an additional set of DPCD capabilities, which can take precedence over the capabilities reported at DP_DPCD_REV. Basically any device supporting DP is going to need to read these in an identical manner, in particular nouveau, so let's go ahead and just move this code out of i915 into a shared DRM DP helper that we can use in other drivers. v2: * Remove redundant dpcd[DP_DPCD_REV] == 0 check * Fix drm_dp_dpcd_read() ret checks Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 77 + drivers/gpu/drm/i915/display/intel_dp.c | 60 +--- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- include/drm/drm_dp_helper.h | 3 + 5 files changed, 83 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 46d88ef4f59ac..9ca88e6c78820 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -433,6 +433,83 @@ static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 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 h could be at a value less than +* the true capability of the panel. The only way to check is to +* then compare h 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 diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0de94fc6289be..284b15f845926 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 h could be at a value less than -* the true capability of the panel. The only way to check is to -* then compare h 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, -
[Intel-gfx] [PATCH v5 11/20] drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect()
For whatever reason we currently unset the EDID for DP CEC support when responding to the connector being unplugged, instead of just doing it in nouveau_connector_detect() where we set the CEC EDID. This isn't really needed and could even potentially cause us to forget to unset the EDID if the connector is removed without a corresponding hpd event, so let's fix that. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index b90591114faaf..4a29f691c08e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -633,10 +633,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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); @@ -1174,8 +1175,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) return NVIF_NOTIFY_DROP; } - if (!plugged) - drm_dp_cec_unset_edid(&nv_connector->aux); NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); drm_helper_hpd_irq_event(connector->dev); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 04/20] drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c
No functional changes. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 8db9216d52c69..4030806e3522b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -50,11 +50,13 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, if (ret != sizeof(dpcd)) return ret; - nv_encoder->dp.link_bw = 27000 * dpcd[1]; - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + 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); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 13/20] drm/i915/dp: Extract drm_dp_read_downstream_info()
We're going to be doing the same probing process in nouveau for determining downstream DP port capabilities, so let's deduplicate the work by moving i915's code for handling this into a shared helper: drm_dp_read_downstream_info(). Note that when we do this, we also do make some functional changes while we're at it: * We always clear the downstream port info before trying to read it, just to make things easier for the caller * We skip reading downstream port info if the DPCD indicates that we don't support downstream port info * We only read as many bytes as needed for the reported number of downstream ports, no sense in reading the whole thing every time v2: * Fixup logic for calculating the downstream port length to account for the fact that downstream port caps can be either 1 byte or 4 bytes long. We can actually skip fixing the max_clock/max_bpc helpers here since they all check for DP_DETAILED_CAP_INFO_AVAILABLE anyway. * Fix ret code check for drm_dp_dpcd_read v5: * Change name from drm_dp_downstream_read_info() to drm_dp_read_downstream_info() * Also, add "See Also" sections for the various downstream info functions (drm_dp_read_downstream_info(), drm_dp_downstream_max_clock(), drm_dp_downstream_max_bpc()) Reviewed-by: Sean Paul Signed-off-by: Lyude Paul squash! drm/i915/dp: Extract drm_dp_read_downstream_info() Signed-off-by: Lyude Paul --- drivers/gpu/drm/drm_dp_helper.c | 62 - drivers/gpu/drm/i915/display/intel_dp.c | 14 +- include/drm/drm_dp_helper.h | 3 ++ 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4c21cf69dad5a..f3643894ad951 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -423,6 +423,56 @@ 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; +} + +/** + * 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 +481,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 +516,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]) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4c7314b7a84e4..9c4b806af8c79 100644 --- a/d
[Intel-gfx] [PATCH v5 07/20] drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable()
Just use drm_dp_dpcd_(readb|writeb)() so we get automatic DPCD logging Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 11 +++ 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 255a281249bc8..612d98fa0a2dc 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1630,19 +1630,22 @@ nv50_sor_disable(struct drm_encoder *encoder, { 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); } } -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 12/20] drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths
Currently we perform both short IRQ handling for DP, and connector reprobing in the HPD IRQ handler. However since we need to grab connection_mutex in order to reprobe a connector, in theory we could accidentally block ourselves from handling any short IRQs until after a modeset completes if a connector hotplug happens to occur in parallel with a modeset. I haven't seen this actually happen yet, but since we're cleaning up nouveau's hotplug handling code anyway and we already have a hpd worker, we can simply fix this by only relying on the HPD worker to actually reprobe connectors when we receive a HPD IRQ. We also add a mask to nouveau_drm to keep track of which connectors are waiting to be reprobed in response to an HPD IRQ. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 42 + drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 70 ++--- drivers/gpu/drm/nouveau/nouveau_display.h | 1 + drivers/gpu/drm/nouveau/nouveau_dp.c| 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + 7 files changed, 86 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4a29f691c08e4..637e91594fbe8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1138,6 +1138,20 @@ 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) { @@ -1147,8 +1161,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - int ret; bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { @@ -1156,31 +1168,9 @@ nouveau_connector_hotplug(struct nvif_notify *notify) 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; - } - - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); - - 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 d6de5cb8e2238..d0b859c4a80ea 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 13016769a194b..bceb48a2dfca6 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[] = { }
[Intel-gfx] [PATCH v5 20/20] drm/nouveau/kms: Start using drm_dp_read_dpcd_caps()
Now that we've extracted i915's code for reading both the normal DPCD caps and extended DPCD caps into a shared helper, let's start using this in nouveau to enable us to start checking extended DPCD caps for free. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 14 ++ 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 59be357b17e00..810bf69565683 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -54,15 +54,13 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, int ret; u8 *dpcd = outp->dp.dpcd; - ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); - if (ret == DP_RECEIVER_CAP_SIZE && dpcd[DP_DPCD_REV]) { - ret = drm_dp_read_desc(aux, &outp->dp.desc, - drm_dp_is_branch(dpcd)); - if (ret < 0) - goto out; - } else { + 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; -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 18/20] drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Currently in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds(), we start the connector probing process by releasing the previous EDID and informing DRM of the change. However, since commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector") drm_connector_update_edid_property() actually checks whether the new EDID we've specified is different from the previous one, and updates the connector's epoch accordingly if it is. But, because we always set the EDID to NULL first in nouveau_connector_ddc_detect() and nouveau_connector_detect_lvds() we end up making DRM think that the EDID changes every single time we do a connector probe - which isn't needed. So, let's fix this by not clearing the EDID at the start of the connector probing process, and instead simply changing or removing it once near the end of the probing process. This will help prevent us from sending unneeded hotplug events to userspace when nothing has actually changed. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 54 ++--- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 637e91594fbe8..49dd0cbc332ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -528,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) { @@ -541,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 @@ -560,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); @@ -609,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); @@ -652,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_connec
[Intel-gfx] [PATCH v5 16/20] drm/i915/dp: Extract drm_dp_read_sink_count()
And of course, we'll also need to read the sink count from other drivers as well if we're checking whether or not it's supported. So, let's extract the code for this into another helper. v2: * Fix drm_dp_dpcd_readb() ret check * Add back comment and move back sink_count assignment in intel_dp_get_dpcd() v5: * Change name from drm_dp_get_sink_count() to drm_dp_read_sink_count() * Also, add "See also:" section to kdocs Signed-off-by: Lyude Paul Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_helper.c | 26 + drivers/gpu/drm/i915/display/intel_dp.c | 11 +-- include/drm/drm_dp_helper.h | 1 + 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 65ff21ae0c271..46d88ef4f59ac 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -733,6 +733,8 @@ EXPORT_SYMBOL(drm_dp_set_subconnector_property); * @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. */ @@ -748,6 +750,30 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector, } 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/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 38318ae935f88..0de94fc6289be 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4648,6 +4648,8 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + int ret; + if (!intel_dp_read_dpcd(intel_dp)) return false; @@ -4664,11 +4666,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } if (intel_dp_has_sink_count(intel_dp)) { - u8 count; - ssize_t r; - - r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count); - if (r < 1) + ret = drm_dp_read_sink_count(&intel_dp->aux); + if (ret < 0) return false; /* @@ -4676,7 +4675,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 diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 4c56ce4dc54fc..bc5cb8c503fbc 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1635,6 +1635,7 @@ struct drm_dp_desc; bool drm_dp_read_sink_count_cap(struct drm_connector *connector, const u8 dpcd[DP_RECEIVER_CAP_SIZE], const struct drm_dp_desc *desc); +int drm_dp_read_sink_count(struct drm_dp_aux *aux); void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); -- 2.26.2 ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v5 17/20] drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT
This is another bit that we never implemented for nouveau: dongle detection. When a "dongle", e.g. an active display adaptor, is hooked up to the system and causes an HPD to be fired, we don't actually know whether or not there's anything plugged into the dongle without checking the sink count. As a result, plugging in a dongle without anything plugged into it currently results in a bogus EDID retrieval error in the kernel log. Additionally, most dongles won't send another long HPD signal if the user suddenly plugs something in, they'll only send a short HPD IRQ with the expectation that the source will check the sink count and reprobe the connector if it's changed - something we don't actually do. As a result, nothing will happen if the user plugs the dongle in before plugging something into the dongle. So, let's fix this by checking the sink count in both nouveau_dp_probe_dpcd() and nouveau_dp_irq(), and reprobing the connector if things change. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 53 --- drivers/gpu/drm/nouveau/nouveau_encoder.h | 2 + 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ad852e572cfec..59be357b17e00 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -36,12 +36,21 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)"); static int nouveau_mst = 1; module_param_named(mst, nouveau_mst, int, 0400); +static bool +nouveau_dp_has_sink_count(struct drm_connector *connector, + struct nouveau_encoder *outp) +{ + 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; @@ -50,9 +59,9 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd)); if (ret < 0) - return connector_status_disconnected; + goto out; } else { - return connector_status_disconnected; + goto out; } if (nouveau_mst) { @@ -61,12 +70,33 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, 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) - return connector_status_disconnected; + goto out; - return connector_status_connected; + status = connector_status_connected; +out: + if (status != connector_status_connected) { + /* Clear any cached info */ + outp->dp.sink_count = 0; + } + return status; } int @@ -159,6 +189,8 @@ void nouveau_dp_irq(struct nouveau_drm *drm, 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; if (!outp) return; @@ -170,12 +202,23 @@ void nouveau_dp_irq(struct nouveau_drm *drm, if (mstm && mstm->is_mst) { if (!nv50_mstm_service(drm, nv_connector, mstm)) - nouveau_connector_hpd(connector); + 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) +
[Intel-gfx] [PULL] topic/nouveau-i915-dp-helpers-and-cleanup
topic/nouveau-i915-dp-helpers-and-cleanup-2020-08-31-1: 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 The following changes since commit bfacb84993eb173c0ab53ca4dd6180f76f4dc176: drm: virtio: fix kconfig dependency warning (2020-08-31 08:55:02 +0200) are available in the Git repository at: git://anongit.freedesktop.org/drm/drm-misc tags/topic/nouveau-i915-dp-helpers-and-cleanup-2020-08-31-1 for you to fetch changes up to 79416e97dda0118b137302575a70a14259a27d7d: drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() (2020-08-31 19:10:09 -0400) 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 Lyude Paul (20): drm/nouveau/kms: Fix some indenting in nouveau_dp_detect() drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc() drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new() drm/nouveau/kms: Search for encoders' connectors properly drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in nv50_sor_disable() drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling drm/i915/dp: Extract drm_dp_read_mst_cap() drm/nouveau/kms: Use new drm_dp_read_mst_cap() helper for checking MST caps drm/nouveau/kms: Move drm_dp_cec_unset_edid() into nouveau_connector_detect() drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths drm/i915/dp: Extract drm_dp_read_downstream_info() drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation drm/i915/dp: Extract drm_dp_read_sink_count_cap() drm/i915/dp: Extract drm_dp_read_sink_count() drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT drm/nouveau/kms: Don't change EDID when it hasn't actually changed drm/i915/dp: Extract drm_dp_read_dpcd_caps() drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() drivers/gpu/drm/drm_dp_helper.c | 187 - drivers/gpu/drm/drm_dp_mst_topology.c | 22 ++ drivers/gpu/drm/i915/display/intel_dp.c | 124 +++ drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- drivers/gpu/drm/nouveau/dispnv04/disp.c | 24 ++- drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/disp.c | 305 drivers/gpu/drm/nouveau/nouveau_connector.c | 132 +--- drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + drivers/gpu/drm/nouveau/nouveau_display.c | 72 ++- drivers/gpu/drm/nouveau/nouveau_display.h | 3 +- drivers/gpu/drm/nouveau/nouveau_dp.c| 210 +++ drivers/gpu/drm/nouveau/nouveau_drm.c | 4 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_encoder.h | 48 - include/drm/drm_dp_helper.h | 15 +- include/drm/drm_dp_mst_helper.h | 3 +- 22 files changed, 779 insertions(+), 393 deletions(-) ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH] drm/dp: start using more of the extended receiver caps
On Tue, 2020-09-01 at 15:32 +0300, Jani Nikula wrote: > In the future, we'll be needing more of the extended receiver capability > field starting at DPCD address 0x2200. (Specifically, we'll need main > link channel coding cap for DP 2.0.) Start using it now to not miss out > later on. > > Cc: Lyude Paul > Signed-off-by: Jani Nikula > > --- > > I guess this can be merged after the topic branch to drm-misc-next or > so, but I'd prefer to have this fairly early on to catch any potential > issues. > --- > drivers/gpu/drm/drm_dp_helper.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c > index 1e7c638873c8..3a3c238452df 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -436,7 +436,7 @@ static u8 drm_dp_downstream_port_count(const u8 > dpcd[DP_RECEIVER_CAP_SIZE]) > static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux, > u8 dpcd[DP_RECEIVER_CAP_SIZE]) > { > - u8 dpcd_ext[6]; > + u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; Not 100% sure this is right? It's not clear at first glance of the 2.0 spec, but my assumption would be that on < DP2.0 devices that everything but those first 6 bytes are zeroed out in the extended DPRX field. Since we memcpy() dpcd_ext using sizeof(dpcd_ext), we'd potentially end up zeroing out all of the DPCD caps that comes after those 6 bytes. > int ret; > > /* -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH 4/5] drm_dp_cec: add plumbing in preparation for MST support
Super minor nitpicks: On Tue, 2020-09-01 at 16:22 +1000, Sam McNally wrote: > From: Hans Verkuil > > Signed-off-by: Hans Verkuil > [sa...@chromium.org: > - rebased > - removed polling-related changes > - moved the calls to drm_dp_cec_(un)set_edid() into the next patch > ] > Signed-off-by: Sam McNally > --- > > .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 2 +- > drivers/gpu/drm/drm_dp_cec.c | 22 ++- > drivers/gpu/drm/i915/display/intel_dp.c | 2 +- > drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- > include/drm/drm_dp_helper.h | 6 +++-- > 5 files changed, 19 insertions(+), 15 deletions(-) > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c > b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c > index 461fa4da0a34..6e7075893ec9 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c > @@ -419,7 +419,7 @@ void amdgpu_dm_initialize_dp_connector(struct > amdgpu_display_manager *dm, > > drm_dp_aux_init(&aconnector->dm_dp_aux.aux); > drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux, > - &aconnector->base); > + &aconnector->base, false); > > if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_eDP) > return; > diff --git a/drivers/gpu/drm/drm_dp_cec.c b/drivers/gpu/drm/drm_dp_cec.c > index 3ab2609f9ec7..04ab7b88055c 100644 > --- a/drivers/gpu/drm/drm_dp_cec.c > +++ b/drivers/gpu/drm/drm_dp_cec.c > @@ -14,6 +14,7 @@ > #include > #include > #include > +#include > > /* > * Unfortunately it turns out that we have a chicken-and-egg situation > @@ -338,8 +339,6 @@ void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const > struct edid *edid) > if (aux->cec.adap) { > if (aux->cec.adap->capabilities == cec_caps && > aux->cec.adap->available_log_addrs == num_las) { > - /* Unchanged, so just set the phys addr */ > - cec_s_phys_addr_from_edid(aux->cec.adap, edid); > goto unlock; > } May as well drop the braces here > /* > @@ -364,15 +363,16 @@ void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const > struct edid *edid) > if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) { > cec_delete_adapter(aux->cec.adap); > aux->cec.adap = NULL; > - } else { > - /* > - * Update the phys addr for the new CEC adapter. When called > - * from drm_dp_cec_register_connector() edid == NULL, so in > - * that case the phys addr is just invalidated. > - */ > - cec_s_phys_addr_from_edid(aux->cec.adap, edid); > } > unlock: > + /* > + * Update the phys addr for the new CEC adapter. When called > + * from drm_dp_cec_register_connector() edid == NULL, so in > + * that case the phys addr is just invalidated. > + */ > + if (aux->cec.adap && edid) { > + cec_s_phys_addr_from_edid(aux->cec.adap, edid); > + } And here > mutex_unlock(&aux->cec.lock); > } > EXPORT_SYMBOL(drm_dp_cec_set_edid); > @@ -418,6 +418,7 @@ EXPORT_SYMBOL(drm_dp_cec_unset_edid); > * drm_dp_cec_register_connector() - register a new connector > * @aux: DisplayPort AUX channel > * @connector: drm connector > + * @is_mst: set to true if this is an MST branch > * > * A new connector was registered with associated CEC adapter name and > * CEC adapter parent device. After registering the name and parent > @@ -425,12 +426,13 @@ EXPORT_SYMBOL(drm_dp_cec_unset_edid); > * CEC and to register a CEC adapter if that is the case. > */ > void drm_dp_cec_register_connector(struct drm_dp_aux *aux, > -struct drm_connector *connector) > + struct drm_connector *connector, bool is_mst) > { > WARN_ON(aux->cec.adap); > if (WARN_ON(!aux->transfer)) > return; > aux->cec.connector = connector; > + aux->cec.is_mst = is_mst; Also JFYI, you can also check aux->is_remote, but maybe you've got another reason for copying this here Either way: Reviewed-by: Lyude Paul ...Also, maybe this is just a coincidence - but do I know your name from somewhere? Perhaps an IRC community from long ago? > INIT_DELAYED_WORK(&aux->cec.unregister_work, &
[Intel-gfx] [RESEND] Requests For Proposals for hosting XDC2021 are now open
(Including a bunch more emails in the To: that got missed the first time) Hello everyone! The X.org board is soliciting proposals to host XDC in 2021. Since XDC2020 is being held virtually this year, we've decided to host in either North America or Europe. However, the board is open to other locations, especially if there's an interesting co-location with another conference. Of course though, due to the ongoing COVID-19 pandemic it's not yet clear whether or not it will be possible to host XDC2021 in person. Because of this, we would like to make it clear that sponsors should prepare for both the possibility of an in person conference, and the possibility of a virtual conference. We will work with organizers on coming up with a deadline for deciding whether or not we'll be going virtual, likely sometime around July. If you're considering hosting XDC, we've assembled a wiki page with what's generally expected and needed: https://www.x.org/wiki/Events/RFP/ When submitting your proposal, please make sure to include at least the key information about the potential location in question, possible dates along with estimated costs. Proposals can be submitted to board at foundation.x.org until the deadline of November 1st. Additionally, an quirk early heads-up to the board if you're considering hosting would be appreciated, in case we need to adjust the schedule a bit. Also, earlier is better since there generally will be a bit of Q&A with organizers. And if you just have some questions about what organizing XDC entails, please feel free to chat with previous organizers, or someone from the board. -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PULL] topic/nouveau-i915-dp-helpers-and-cleanup
On Fri, 2020-09-04 at 09:24 -0400, Rodrigo Vivi wrote: > On Mon, Aug 31, 2020 at 07:38:57PM -0400, Lyude Paul wrote: > > topic/nouveau-i915-dp-helpers-and-cleanup-2020-08-31-1: > > 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 > > was this picked-up on the nouveau side already? > whenever that happens, please ping me so I can pull this to dinq. Everything that's needed is in this topic branch > > But a reminder that it has my ack to go to drm-misc or only to nouveau > directly. > > > The following changes since commit bfacb84993eb173c0ab53ca4dd6180f76f4dc176: > > > > drm: virtio: fix kconfig dependency warning (2020-08-31 08:55:02 +0200) > > > > are available in the Git repository at: > > > > git://anongit.freedesktop.org/drm/drm-misc tags/topic/nouveau-i915-dp- > > helpers-and-cleanup-2020-08-31-1 > > > > for you to fetch changes up to 79416e97dda0118b137302575a70a14259a27d7d: > > > > drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() (2020-08-31 19:10:09 > > -0400) > > > > > > 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 > > > > > > Lyude Paul (20): > > drm/nouveau/kms: Fix some indenting in nouveau_dp_detect() > > drm/nouveau/kms/nv50-: Remove open-coded drm_dp_read_desc() > > drm/nouveau/kms/nv50-: Just use drm_dp_dpcd_read() in nouveau_dp.c > > drm/nouveau/kms/nv50-: Use macros for DP registers in nouveau_dp.c > > drm/nouveau/kms: Don't clear DP_MST_CTRL DPCD in nv50_mstm_new() > > drm/nouveau/kms: Search for encoders' connectors properly > > drm/nouveau/kms/nv50-: Use drm_dp_dpcd_(readb|writeb)() in > > nv50_sor_disable() > > drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling > > drm/i915/dp: Extract drm_dp_read_mst_cap() > > drm/nouveau/kms: Use new drm_dp_read_mst_cap() helper for checking MST > > caps > > drm/nouveau/kms: Move drm_dp_cec_unset_edid() into > > nouveau_connector_detect() > > drm/nouveau/kms: Only use hpd_work for reprobing in HPD paths > > drm/i915/dp: Extract drm_dp_read_downstream_info() > > drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode > > validation > > drm/i915/dp: Extract drm_dp_read_sink_count_cap() > > drm/i915/dp: Extract drm_dp_read_sink_count() > > drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT > > drm/nouveau/kms: Don't change EDID when it hasn't actually changed > > drm/i915/dp: Extract drm_dp_read_dpcd_caps() > > drm/nouveau/kms: Start using drm_dp_read_dpcd_caps() > > > > drivers/gpu/drm/drm_dp_helper.c | 187 - > > drivers/gpu/drm/drm_dp_mst_topology.c | 22 ++ > > drivers/gpu/drm/i915/display/intel_dp.c | 124 +++ > > drivers/gpu/drm/i915/display/intel_dp.h | 1 - > > drivers/gpu/drm/i915/display/intel_lspcon.c | 2 +- > > drivers/gpu/drm/nouveau/dispnv04/dac.c | 2 +- > > drivers/gpu/drm/nouveau/dispnv04/dfp.c | 7 +- > > drivers/gpu/drm/nouveau/dispnv04/disp.c | 24 ++- > > drivers/gpu/drm/nouveau/dispnv04/disp.h | 4 + > > drivers/gpu/drm/nouveau/dispnv04/tvnv04.c | 2 +- > > drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 2 +- > > drivers/gpu/drm/nouveau/dispnv50/disp.c | 305 - > > --- > > drivers/gpu/drm/nouveau/nouveau_connector.c | 132 +--- > > drivers/gpu/drm/nouveau/nouveau_connector.h | 1 + > > drivers/gpu/drm/nouveau/nouveau_display.c | 72 ++- > > drivers/gpu/drm/nouveau/nouveau_display.
Re: [Intel-gfx] [PATCH v2 00/18] drm/i915: Pimp DP DFP handling
Will try to look at this today, if I don't have the time though I'll definitely have the time on Tuesday On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä > > Attempt to deal with DP downstream facing ports (DFP) more > thoroughly. This involves reading more of the port caps > and dealing with various clock/bpc limitations. > > And we try to enable YCbCr 444->420 conversion for HDMI DFPs > which could allow some 4k displays to actually use 4k on > pre-icl hardware (which doesn't have native 420 output), > assuming we don't run into some other hardware limits. > > I dropped my earlier patches to also hook in the DP dual mode > adapter probing since sadly I've not actually seen a DP->DP++ > dongle that passes through the i2c traffic for those. > > Only pimped the SST side of things. Not sure what would > be required to get it all working for MST. > > Ville Syrjälä (18): > drm/dp: Dump downstream facing port caps > drm/i915/lspcon: Do not send infoframes to non-HDMI sinks > drm/dp: Define protocol converter DPCD registers > drm/dp: Define more downstream facing port caps > drm/i915: Reworkd DFP max bpc handling > drm/dp: Add helpers to identify downstream facing port types > drm/dp: Pimp drm_dp_downstream_max_bpc() > drm/dp: Redo drm_dp_downstream_max_clock() as > drm_dp_downstream_max_dotclock() > drm/i915: Reworkd DP DFP clock handling > drm/dp: Add drm_dp_downstream_{min,max}_tmds_clock() > drm/i915: Deal with TMDS DFP clock limits > drm/i915: Configure DP 1.3+ protocol converted HDMI mode > drm/dp: Add drm_dp_downstream_mode() > drm/i915: Handle downstream facing ports w/o EDID > drm/i915: Extract intel_hdmi_has_audio() > drm/i915: DP->HDMI TMDS clock limits vs. deep color > drm/dp: Add helpers for DFP YCbCr 4:2:0 handling > drm/i915: Do YCbCr 444->420 conversion via DP protocol converters > > drivers/gpu/drm/drm_dp_helper.c | 382 +++--- > drivers/gpu/drm/drm_edid.c| 19 + > drivers/gpu/drm/i915/display/intel_ddi.c | 11 +- > .../drm/i915/display/intel_display_debugfs.c | 3 +- > .../drm/i915/display/intel_display_types.h| 9 + > drivers/gpu/drm/i915/display/intel_dp.c | 304 +++--- > drivers/gpu/drm/i915/display/intel_dp.h | 1 + > drivers/gpu/drm/i915/display/intel_hdmi.c | 82 ++-- > drivers/gpu/drm/i915/display/intel_hdmi.h | 2 + > include/drm/drm_dp_helper.h | 63 ++- > include/drm/drm_edid.h| 4 + > 11 files changed, 738 insertions(+), 142 deletions(-) > -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 06/18] drm/dp: Add helpers to identify downstream facing port types
On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä > > Add a few helpers to let us better identify which kind of DFP > we're dealing with. > > Signed-off-by: Ville Syrjälä > --- > drivers/gpu/drm/drm_dp_helper.c | 60 + > include/drm/drm_dp_helper.h | 5 +++ > 2 files changed, 65 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > b/drivers/gpu/drm/drm_dp_helper.c > index c21bbfc3d714..0fcb94f7dbe5 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -363,6 +363,66 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux > *aux, > } > EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); > > +static bool is_edid_digital_input_dp(const struct edid *edid) > +{ > + return edid && edid->revision >= 4 && > + edid->input & DRM_EDID_INPUT_DIGITAL && > + (edid->input & DRM_EDID_DIGITAL_TYPE_MASK) == > DRM_EDID_DIGITAL_TYPE_DP; > +} > + > +/** > + * drm_dp_downstream_is_type() - is the downstream facing port of certain > type? > + * @dpcd: DisplayPort configuration data > + * @port_cap: port capabilities > + * > + * Caveat: Only works with DPCD 1.1+ port caps. > + * > + * Returns whether the downstream facing port matches the type. Nitpick: s/Returns/Returns:/ for kdoc purposes > + */ > +bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4], u8 type) > +{ > + return drm_dp_is_branch(dpcd) && > + dpcd[DP_DPCD_REV] >= 0x11 && > + (port_cap[0] & DP_DS_PORT_TYPE_MASK) == type; > +} > +EXPORT_SYMBOL(drm_dp_downstream_is_type); > + > +/** > + * drm_dp_downstream_is_tmds() - is the downstream facing port TMDS? > + * @dpcd: DisplayPort configuration data > + * @port_cap: port capabilities > + * @edid: EDID > + * > + * Returns whether the downstream facing port is TMDS (HDMI/DVI). Same nitpick here > + */ > +bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4], > +const struct edid *edid) > +{ > + if (dpcd[DP_DPCD_REV] < 0x11) { > + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & > DP_DWN_STRM_PORT_TYPE_MASK) { > + case DP_DWN_STRM_PORT_TYPE_TMDS: > + return true; > + default: > + return false; > + } > + } > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_DP_DUALMODE: > + if (is_edid_digital_input_dp(edid)) > + return false; > + fallthrough; > + case DP_DS_PORT_TYPE_DVI: > + case DP_DS_PORT_TYPE_HDMI: > + return true; > + default: > + return false; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_is_tmds); > + > /** > * drm_dp_send_real_edid_checksum() - send back real edid checksum value > * @aux: DisplayPort AUX channel > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 86461a40066b..4f946826dfce 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1638,6 +1638,11 @@ bool drm_dp_send_real_edid_checksum(struct drm_dp_aux > *aux, > 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]); > +bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4], u8 type); > +bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4], > +const struct edid *edid); > int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 07/18] drm/dp: Pimp drm_dp_downstream_max_bpc()
ENT] & >DP_DETAILED_CAP_INFO_AVAILABLE; > @@ -789,7 +802,7 @@ void drm_dp_downstream_debug(struct seq_file *m, > seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", > clk); > } > > - bpc = drm_dp_downstream_max_bpc(dpcd, port_cap); > + bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); > > if (bpc > 0) > seq_printf(m, "\t\tMax bpc: %d\n", bpc); > diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c > b/drivers/gpu/drm/i915/display/intel_display_debugfs.c > index 53a0a3d9a22d..0bf31f9a8af5 100644 > --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c > +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c > @@ -626,6 +626,7 @@ static void intel_dp_info(struct seq_file *m, > { > struct intel_encoder *intel_encoder = > intel_attached_encoder(intel_connector); > struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder); > + const struct drm_property_blob *edid = intel_connector- > >base.edid_blob_ptr; > > seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]); > seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio)); > @@ -633,7 +634,7 @@ static void intel_dp_info(struct seq_file *m, > intel_panel_info(m, &intel_connector->panel); > > drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports, > - &intel_dp->aux); > + edid ? edid->data : NULL, &intel_dp->aux); > } > > static void intel_dp_mst_info(struct seq_file *m, > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > b/drivers/gpu/drm/i915/display/intel_dp.c > index 2c8e82d97a34..c73b3efd84e0 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -6071,7 +6071,7 @@ intel_dp_set_edid(struct intel_dp *intel_dp) > > intel_dp->dfp.max_bpc = > drm_dp_downstream_max_bpc(intel_dp->dpcd, > - intel_dp->downstream_ports); > + intel_dp->downstream_ports, edid); > > drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] DFP max bpc %d\n", > connector->base.base.id, connector->base.name, > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 4f946826dfce..6218de1294c1 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1646,10 +1646,14 @@ bool drm_dp_downstream_is_tmds(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4]); > + const u8 port_cap[4], > + const struct edid *edid); > int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); > -void drm_dp_downstream_debug(struct seq_file *m, const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4], struct drm_dp_aux *aux); > +void drm_dp_downstream_debug(struct seq_file *m, > + const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid, > + struct drm_dp_aux *aux); > enum drm_mode_subconnector > drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], >const u8 port_cap[4]); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 07/18] drm/dp: Pimp drm_dp_downstream_max_bpc()
return 0; > + return 8; > } > } > EXPORT_SYMBOL(drm_dp_downstream_max_bpc); > @@ -717,12 +727,15 @@ EXPORT_SYMBOL(drm_dp_downstream_id); > * @m: pointer for debugfs file > * @dpcd: DisplayPort configuration data > * @port_cap: port capabilities > + * @edid: EDID > * @aux: DisplayPort AUX channel > * > */ > void drm_dp_downstream_debug(struct seq_file *m, >const u8 dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4], struct drm_dp_aux *aux) > + const u8 port_cap[4], > + const struct edid *edid, > + struct drm_dp_aux *aux) > { > bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & >DP_DETAILED_CAP_INFO_AVAILABLE; > @@ -789,7 +802,7 @@ void drm_dp_downstream_debug(struct seq_file *m, > seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", > clk); > } > > - bpc = drm_dp_downstream_max_bpc(dpcd, port_cap); > + bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); > > if (bpc > 0) > seq_printf(m, "\t\tMax bpc: %d\n", bpc); > diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c > b/drivers/gpu/drm/i915/display/intel_display_debugfs.c > index 53a0a3d9a22d..0bf31f9a8af5 100644 > --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c > +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c > @@ -626,6 +626,7 @@ static void intel_dp_info(struct seq_file *m, > { > struct intel_encoder *intel_encoder = > intel_attached_encoder(intel_connector); > struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder); > + const struct drm_property_blob *edid = intel_connector- > >base.edid_blob_ptr; > > seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]); > seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio)); > @@ -633,7 +634,7 @@ static void intel_dp_info(struct seq_file *m, > intel_panel_info(m, &intel_connector->panel); > > drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports, > - &intel_dp->aux); > + edid ? edid->data : NULL, &intel_dp->aux); > } > > static void intel_dp_mst_info(struct seq_file *m, > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > b/drivers/gpu/drm/i915/display/intel_dp.c > index 2c8e82d97a34..c73b3efd84e0 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -6071,7 +6071,7 @@ intel_dp_set_edid(struct intel_dp *intel_dp) > > intel_dp->dfp.max_bpc = > drm_dp_downstream_max_bpc(intel_dp->dpcd, > - intel_dp->downstream_ports); > + intel_dp->downstream_ports, edid); > > drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] DFP max bpc %d\n", > connector->base.base.id, connector->base.name, > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 4f946826dfce..6218de1294c1 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1646,10 +1646,14 @@ bool drm_dp_downstream_is_tmds(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4]); > + const u8 port_cap[4], > + const struct edid *edid); > int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); > -void drm_dp_downstream_debug(struct seq_file *m, const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4], struct drm_dp_aux *aux); > +void drm_dp_downstream_debug(struct seq_file *m, > + const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid, > + struct drm_dp_aux *aux); > enum drm_mode_subconnector > drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], >const u8 port_cap[4]); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 08/18] drm/dp: Redo drm_dp_downstream_max_clock() as drm_dp_downstream_max_dotclock()
dotclk = min(max_dotclk, ds_max_dotclk); > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 6218de1294c1..19bc04207788 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1643,8 +1643,8 @@ bool drm_dp_downstream_is_type(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); > -int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > - const u8 port_cap[4]); > +int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4]); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 10/18] drm/dp: Add drm_dp_downstream_{min, max}_tmds_clock()
t; + return 25000; > + default: > + return 0; > + } > + } > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_DP_DUALMODE: > + if (is_edid_digital_input_dp(edid)) > + return 0; > + fallthrough; > + case DP_DS_PORT_TYPE_DVI: > + case DP_DS_PORT_TYPE_HDMI: > + /* > + * Unclear whether the protocol converter could > + * utilize pixel replication. Assume it won't. > + */ > + return 25000; > + default: > + return 0; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_min_tmds_clock); > + > /** >* drm_dp_downstream_max_bpc() - extract downstream facing port max >* bits per component > @@ -788,6 +896,14 @@ void drm_dp_downstream_debug(struct seq_file *m, > if (clk > 0) > seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); > > + clk = drm_dp_downstream_max_tmds_clock(dpcd, port_cap, edid); > + if (clk > 0) > + seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); > + > + clk = drm_dp_downstream_min_tmds_clock(dpcd, port_cap, edid); > + if (clk > 0) > + seq_printf(m, "\t\tMin TMDS clock: %d kHz\n", clk); > + > bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); > > if (bpc > 0) > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 19bc04207788..6812a3e0de8d 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1645,6 +1645,12 @@ bool drm_dp_downstream_is_tmds(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > const struct edid *edid); > int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); > +int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid); > +int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 10/18] drm/dp: Add drm_dp_downstream_{min, max}_tmds_clock()
default: > + return 0; > + } > + } > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_DP_DUALMODE: > + if (is_edid_digital_input_dp(edid)) > + return 0; > + fallthrough; > + case DP_DS_PORT_TYPE_DVI: > + case DP_DS_PORT_TYPE_HDMI: > + /* > + * Unclear whether the protocol converter could > + * utilize pixel replication. Assume it won't. > + */ > + return 25000; > + default: > + return 0; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_min_tmds_clock); > + > /** >* drm_dp_downstream_max_bpc() - extract downstream facing port max >* bits per component > @@ -788,6 +896,14 @@ void drm_dp_downstream_debug(struct seq_file *m, > if (clk > 0) > seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); > > + clk = drm_dp_downstream_max_tmds_clock(dpcd, port_cap, edid); > + if (clk > 0) > + seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); > + > + clk = drm_dp_downstream_min_tmds_clock(dpcd, port_cap, edid); > + if (clk > 0) > + seq_printf(m, "\t\tMin TMDS clock: %d kHz\n", clk); > + > bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); > > if (bpc > 0) > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 19bc04207788..6812a3e0de8d 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1645,6 +1645,12 @@ bool drm_dp_downstream_is_tmds(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > const struct edid *edid); > int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); > +int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid); > +int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4], > + const struct edid *edid); > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 12/18] drm/i915: Configure DP 1.3+ protocol converted HDMI mode
On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä > > DP 1.3 adds some extra control knobs for DP->HDMI protocol conversion. > Let's use that to configure the "HDMI mode" (ie. infoframes vs. not) > based on the capabilities of the sink. > > Signed-off-by: Ville Syrjälä > --- > drivers/gpu/drm/i915/display/intel_ddi.c | 1 + > drivers/gpu/drm/i915/display/intel_dp.c | 23 +++ > drivers/gpu/drm/i915/display/intel_dp.h | 1 + > 3 files changed, 25 insertions(+) > > diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c > b/drivers/gpu/drm/i915/display/intel_ddi.c > index 28ff85493f25..9adba0d0b4aa 100644 > --- a/drivers/gpu/drm/i915/display/intel_ddi.c > +++ b/drivers/gpu/drm/i915/display/intel_ddi.c > @@ -3470,6 +3470,7 @@ static void hsw_ddi_pre_enable_dp(struct > intel_atomic_state *state, > intel_ddi_init_dp_buf_reg(encoder); > if (!is_mst) > intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); > + intel_dp_configure_protocol_converter(intel_dp); > intel_dp_sink_set_decompression_state(intel_dp, crtc_state, > true); > intel_dp_sink_set_fec_ready(intel_dp, crtc_state); > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > b/drivers/gpu/drm/i915/display/intel_dp.c > index a703e4659e47..047449253a54 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -3792,6 +3792,28 @@ static void intel_dp_enable_port(struct intel_dp > *intel_dp, > intel_de_posting_read(dev_priv, intel_dp->output_reg); > } > > +void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp) > +{ > + struct drm_i915_private *i915 = dp_to_i915(intel_dp); > + u8 tmp; > + > + if (intel_dp->dpcd[DP_DPCD_REV] < 0x13) > + return; > + > + if (!drm_dp_is_branch(intel_dp->dpcd)) > + return; > + > + tmp = intel_dp->has_hdmi_sink ? > + DP_HDMI_DVI_OUTPUT_CONFIG : 0; > + > + if (drm_dp_dpcd_writeb(&intel_dp->aux, > +DP_PROTOCOL_CONVERTER_CONTROL_0, tmp) <= 0) > + drm_dbg_kms(&i915->drm, "Failed to set protocol converter HDMI > mode to %s\n", > + enableddisabled(intel_dp->has_hdmi_sink)); > + > + /* TODO: configure YCbCr 4:2:2/4:2:0 conversion */ > +} Maybe add a DP helper for this while we're at it? Up to you > + > static void intel_enable_dp(struct intel_atomic_state *state, > struct intel_encoder *encoder, > const struct intel_crtc_state *pipe_config, > @@ -3829,6 +3851,7 @@ static void intel_enable_dp(struct intel_atomic_state > *state, > } > > intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); > + intel_dp_configure_protocol_converter(intel_dp); > intel_dp_start_link_train(intel_dp); > intel_dp_stop_link_train(intel_dp); > > diff --git a/drivers/gpu/drm/i915/display/intel_dp.h > b/drivers/gpu/drm/i915/display/intel_dp.h > index ec5688a21f66..08a1c0aa8b94 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.h > +++ b/drivers/gpu/drm/i915/display/intel_dp.h > @@ -51,6 +51,7 @@ int intel_dp_get_link_train_fallback_values(struct > intel_dp *intel_dp, > int intel_dp_retrain_link(struct intel_encoder *encoder, > struct drm_modeset_acquire_ctx *ctx); > void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); > +void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp); > void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp, > const struct intel_crtc_state > *crtc_state, > bool enable); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 13/18] drm/dp: Add drm_dp_downstream_mode()
ID_1920x1080i_60(3 << 4) > +# define DP_DS_NON_EDID_1920x1080i_50(4 << 4) > +# define DP_DS_NON_EDID_1280x720_60 (5 << 4) > +# define DP_DS_NON_EDID_1280x720_50 (7 << 4) > /* offset 1 for VGA is maximum megapixels per second / 8 */ > /* offset 1 for DVI/HDMI is maximum TMDS clock in Mbps / 2.5 */ > /* offset 2 for VGA/DVI/HDMI */ > @@ -1654,6 +1663,9 @@ int drm_dp_downstream_min_tmds_clock(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); > +struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev, > + const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4]); > int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); > void drm_dp_downstream_debug(struct seq_file *m, >const u8 dpcd[DP_RECEIVER_CAP_SIZE], > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index cfa4f5af49af..b27a0e2169c8 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -517,4 +517,8 @@ void drm_edid_get_monitor_name(struct edid *edid, char > *name, > struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, > int hsize, int vsize, int fresh, > bool rb); > +struct drm_display_mode * > +drm_display_mode_from_cea_vic(struct drm_device *dev, > + u8 video_code); > + > #endif /* __DRM_EDID_H__ */ -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 17/18] drm/dp: Add helpers for DFP YCbCr 4:2:0 handling
On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä > > Add helpers to determine whether the DFP supports > YCbCr 4:2:0 passthrough or YCbCr 4:4:4->4:2:0 conversion. > > Signed-off-by: Ville Syrjälä > --- > drivers/gpu/drm/drm_dp_helper.c | 44 + > include/drm/drm_dp_helper.h | 8 ++ > 2 files changed, 52 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > b/drivers/gpu/drm/drm_dp_helper.c > index 0d5e9bcf11d0..dc68e10aa1fd 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -808,6 +808,50 @@ int drm_dp_downstream_max_bpc(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > } > EXPORT_SYMBOL(drm_dp_downstream_max_bpc); > > +bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4]) > +{ > + if (!drm_dp_is_branch(dpcd)) > + return false; > + > + if (dpcd[DP_DPCD_REV] < 0x13) > + return false; > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_DP: > + return true; > + case DP_DS_PORT_TYPE_HDMI: > + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & > DP_DETAILED_CAP_INFO_AVAILABLE) == 0) > + return false; > + > + return port_cap[3] & DP_DS_HDMI_YCBCR420_PASS_THROUGH; > + default: > + return false; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_420_passthrough); Forgot the kdocs again > + > +bool drm_dp_downstream_444_to_420_conversion(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4]) > +{ > + if (!drm_dp_is_branch(dpcd)) > + return false; > + > + if (dpcd[DP_DPCD_REV] < 0x13) > + return false; > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_HDMI: > + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & > DP_DETAILED_CAP_INFO_AVAILABLE) == 0) > + return false; > + > + return port_cap[3] & DP_DS_HDMI_YCBCR444_TO_420_CONV; > + default: > + return false; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion); > + > /** > * drm_dp_downstream_mode() - return a mode for downstream facing port > * @dpcd: DisplayPort configuration data > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index fbba4a0f7366..c9f2851904d0 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -407,6 +407,10 @@ struct drm_device; > # define DP_DS_DVI_HIGH_COLOR_DEPTH (1 << 2) > /* offset 3 for HDMI */ > # define DP_DS_HDMI_FRAME_SEQ_TO_FRAME_PACK (1 << 0) > +# define DP_DS_HDMI_YCBCR422_PASS_THROUGH (1 << 1) > +# define DP_DS_HDMI_YCBCR420_PASS_THROUGH (1 << 2) > +# define DP_DS_HDMI_YCBCR444_TO_422_CONV(1 << 3) > +# define DP_DS_HDMI_YCBCR444_TO_420_CONV(1 << 4) > > #define DP_MAX_DOWNSTREAM_PORTS 0x10 > > @@ -1663,6 +1667,10 @@ int drm_dp_downstream_min_tmds_clock(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); > +bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > +const u8 port_cap[4]); > +bool drm_dp_downstream_444_to_420_conversion(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4]); > struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev, > const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4]); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 00/18] drm/i915: Pimp DP DFP handling
With the nitpicks addressed (note there were a couple of other spots where we wanted to use Return: in the kdocs, but I didn't bother pointing all of them out), all but patch 07 is: Reviewed-by: Lyude Paul On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä > > Attempt to deal with DP downstream facing ports (DFP) more > thoroughly. This involves reading more of the port caps > and dealing with various clock/bpc limitations. > > And we try to enable YCbCr 444->420 conversion for HDMI DFPs > which could allow some 4k displays to actually use 4k on > pre-icl hardware (which doesn't have native 420 output), > assuming we don't run into some other hardware limits. > > I dropped my earlier patches to also hook in the DP dual mode > adapter probing since sadly I've not actually seen a DP->DP++ > dongle that passes through the i2c traffic for those. > > Only pimped the SST side of things. Not sure what would > be required to get it all working for MST. > > Ville Syrjälä (18): > drm/dp: Dump downstream facing port caps > drm/i915/lspcon: Do not send infoframes to non-HDMI sinks > drm/dp: Define protocol converter DPCD registers > drm/dp: Define more downstream facing port caps > drm/i915: Reworkd DFP max bpc handling > drm/dp: Add helpers to identify downstream facing port types > drm/dp: Pimp drm_dp_downstream_max_bpc() > drm/dp: Redo drm_dp_downstream_max_clock() as > drm_dp_downstream_max_dotclock() > drm/i915: Reworkd DP DFP clock handling > drm/dp: Add drm_dp_downstream_{min,max}_tmds_clock() > drm/i915: Deal with TMDS DFP clock limits > drm/i915: Configure DP 1.3+ protocol converted HDMI mode > drm/dp: Add drm_dp_downstream_mode() > drm/i915: Handle downstream facing ports w/o EDID > drm/i915: Extract intel_hdmi_has_audio() > drm/i915: DP->HDMI TMDS clock limits vs. deep color > drm/dp: Add helpers for DFP YCbCr 4:2:0 handling > drm/i915: Do YCbCr 444->420 conversion via DP protocol converters > > drivers/gpu/drm/drm_dp_helper.c | 382 +++--- > drivers/gpu/drm/drm_edid.c| 19 + > drivers/gpu/drm/i915/display/intel_ddi.c | 11 +- > .../drm/i915/display/intel_display_debugfs.c | 3 +- > .../drm/i915/display/intel_display_types.h| 9 + > drivers/gpu/drm/i915/display/intel_dp.c | 304 +++--- > drivers/gpu/drm/i915/display/intel_dp.h | 1 + > drivers/gpu/drm/i915/display/intel_hdmi.c | 82 ++-- > drivers/gpu/drm/i915/display/intel_hdmi.h | 2 + > include/drm/drm_dp_helper.h | 63 ++- > include/drm/drm_edid.h| 4 + > 11 files changed, 738 insertions(+), 142 deletions(-) > -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH v2 07/18] drm/dp: Pimp drm_dp_downstream_max_bpc()
On Thu, 2020-09-10 at 17:46 +0300, Ville Syrjälä wrote: > On Tue, Sep 08, 2020 at 01:51:56PM -0400, Lyude Paul wrote: > > On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > > > From: Ville Syrjälä > > > > > > Deal with more cases in drm_dp_downstream_max_bpc(): > > > - DPCD 1.0 -> assume 8bpc for non-DP > > > - DPCD 1.1+ DP (or DP++ with DP sink) -> allow anything > > > - DPCD 1.1+ TMDS -> check the caps, assume 8bpc if the value is crap > > > - anything else -> assume 8bpc > > > > > > Signed-off-by: Ville Syrjälä > > > --- > > > drivers/gpu/drm/drm_dp_helper.c | 69 +++ > > > .../drm/i915/display/intel_display_debugfs.c | 3 +- > > > drivers/gpu/drm/i915/display/intel_dp.c | 2 +- > > > include/drm/drm_dp_helper.h | 10 ++- > > > 4 files changed, 51 insertions(+), 33 deletions(-) > > > > > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > > > b/drivers/gpu/drm/drm_dp_helper.c > > > index 0fcb94f7dbe5..ab87209c25d8 100644 > > > --- a/drivers/gpu/drm/drm_dp_helper.c > > > +++ b/drivers/gpu/drm/drm_dp_helper.c > > > @@ -653,36 +653,44 @@ int drm_dp_downstream_max_clock(const u8 > > > dpcd[DP_RECEIVER_CAP_SIZE], > > > EXPORT_SYMBOL(drm_dp_downstream_max_clock); > > > > > > /** > > > - * drm_dp_downstream_max_bpc() - extract branch device max > > > - * bits per component > > > - * @dpcd: DisplayPort configuration data > > > - * @port_cap: port capabilities > > > - * > > > - * See also: > > > - * drm_dp_read_downstream_info() > > > - * drm_dp_downstream_max_clock() > > > - * > > > - * Returns: Max bpc on success or 0 if max bpc not defined > > > - */ > > > + * drm_dp_downstream_max_bpc() - extract downstream facing port max > > > + * bits per component > > > + * @dpcd: DisplayPort configuration data > > > + * @port_cap: downstream facing port capabilities > > > + * @edid: EDID > > > + * > > > + * 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]) > > > + const u8 port_cap[4], > > > + const struct edid *edid) > > > { > > > - int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; > > > - bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & > > > - DP_DETAILED_CAP_INFO_AVAILABLE; > > > - int bpc; > > > - > > > - if (!detailed_cap_info) > > > > I don't think we can drop this check. There's a somewhat surprising blurb > > about downstream port caps in the DP 2.0 spec (section 5.3.3.1): > > > >In addition, the adapter shall set the Detailed Capabilities Info > > registers > >(DPCD Addresses 00080h through 0008Fh) to show all the downstream types, > >including DFP 0. Either one or four bytes are used, per DFP type > >indication. Therefore, up to 16 (with 1-byte descriptor) or four (with 4- > >byte descriptor) DFP capabilities can be stored. > > > > I've never once actually seen a sink do this, but this does mean it's > > technically possible tthat if we don't check the detailed caps bit then we > > might end up reading another port's DFP type instead of max_bpc info. Note > > though that we can make the assumption the four byte version of the field is > > used for DP 1.4+ > > The check is now ... > > > > > + if (!drm_dp_is_branch(dpcd)) > > > return 0; > > > > > > - switch (type) { > > > - case DP_DS_PORT_TYPE_VGA: > > > - case DP_DS_PORT_TYPE_DVI: > > > - case DP_DS_PORT_TYPE_HDMI: > > > + if (dpcd[DP_DPCD_REV] < 0x11) { > > > + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & > > > DP_DWN_STRM_PORT_TYPE_MASK) { > > > + case DP_DWN_STRM_PORT_TYPE_DP: > > > + return 0; > > > + default: > > > + return 8; > > > + } > > > + } > > > + > > > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > > > + case DP_DS_PORT_TYPE_DP: > > > + return 0; > > > case DP_DS_PORT_TYPE_DP_DUALMODE: > > > -
Re: [Intel-gfx] [PATCH v2 10/18] drm/dp: Add drm_dp_downstream_{min, max}_tmds_clock()
On Thu, 2020-09-10 at 16:55 +0300, Ville Syrjälä wrote: > On Tue, Sep 08, 2020 at 02:08:17PM -0400, Lyude Paul wrote: > > On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > > > From: Ville Syrjälä > > > > > > Add helpers to get the TMDS clock limits for HDMI/DVI downstream > > > facing ports. > > > > > > Signed-off-by: Ville Syrjälä > > > --- > > > drivers/gpu/drm/drm_dp_helper.c | 116 > > > include/drm/drm_dp_helper.h | 6 ++ > > > 2 files changed, 122 insertions(+) > > > > > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > > > b/drivers/gpu/drm/drm_dp_helper.c > > > index 822a30e609ef..f567428f2aef 100644 > > > --- a/drivers/gpu/drm/drm_dp_helper.c > > > +++ b/drivers/gpu/drm/drm_dp_helper.c > > > @@ -643,6 +643,114 @@ int drm_dp_downstream_max_dotclock(const u8 > > > dpcd[DP_RECEIVER_CAP_SIZE], > > > } > > > EXPORT_SYMBOL(drm_dp_downstream_max_dotclock); > > > > > > +/** > > > + * drm_dp_downstream_max_tmds_clock() - extract downstream facing port > > > max > > > TMDS clock > > > + * @dpcd: DisplayPort configuration data > > > + * @port_cap: port capabilities > > > + * @edid: EDID > > > + * > > > + * Returns HDMI/DVI downstream facing port max TMDS clock in kHz on > > > success, > > > + * or 0 if max TMDS clock not defined > > > + */ > > > +int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > > > + const u8 port_cap[4], > > > + const struct edid *edid) > > > +{ > > > + if (!drm_dp_is_branch(dpcd)) > > > + return 0; > > > + > > > + if (dpcd[DP_DPCD_REV] < 0x11) { > > > + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & > > > DP_DWN_STRM_PORT_TYPE_MASK) { > > > + case DP_DWN_STRM_PORT_TYPE_TMDS: > > > + return 165000; > > > + default: > > > + return 0; > > > + } > > > + } > > > + > > > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > > > + case DP_DS_PORT_TYPE_DP_DUALMODE: > > > + if (is_edid_digital_input_dp(edid)) > > > + return 0; > > > + /* > > > + * It's left up to the driver to check the > > > + * DP dual mode adapter's max TMDS clock. > > > + * > > > + * Unfortunatley it looks like branch devices > > > + * may not fordward that the DP dual mode i2c > > > + * access so we just usually get i2c nak :( > > > + */ > > > + fallthrough; > > > + case DP_DS_PORT_TYPE_HDMI: > > > + /* > > > + * We should perhaps assume 165 MHz when detailed cap > > > + * info is not available. But looks like many typical > > > + * branch devices fall into that category and so we'd > > > + * probably end up with users complaining that they can't > > > + * get high resolution modes with their favorite dongle. > > > + * > > > + * So let's limit to 300 MHz instead since DPCD 1.4 > > > + * HDMI 2.0 DFPs are required to have the detailed cap > > > + * info. So it's more likely we're dealing with a HDMI 1.4 > > > + * compatible* device here. > > > > Forgot to mention - not directly related to this series, there's some hidden > > i2c bits that I think can also be probed for this sort of information on > > passive adapters, I know amdgpu actually supports this. I wonder how many of > > them also apply to older active adapters... > > Something other than the normal DP dual mode stuff? Actually that -may- have been what I was thinking of but I'm not sure, I'd probably need to look through DAL to find out > -- Sincerely, Lyude Paul (she/her) Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] [PATCH] Revert "drm/display/dp_mst: Move all payload info into the atomic state"
On Fri, 2023-01-13 at 11:25 +0100, Daniel Vetter wrote: > On Fri, Jan 13, 2023 at 12:16:57PM +0200, Jani Nikula wrote: > > > > Cc: intel-gfx, drm maintainers > > > > Please have the courtesy of Cc'ing us for changes impacting us, and > > maybe try to involve us earlier instead of surprising us like > > this. Looks like this has been debugged for at least three months, and > > the huge revert at this point is going to set us back with what's been > > developed on top of it for i915 DP MST and DSC. > > tbf I assumed this wont land when I've seen it fly by. It feels a bit much > like living under a rock for half a year and then creating a mess for > everyone else who's been building on top of this is not great. > > Like yes it's a regression, but apparently not a blantantly obvious one, > and I think if we just ram this in there's considerable community goodwill > down the drain. Someone needs to get that goodwill up the drain again. > > > It's a regression, I get that, but this is also going to be really nasty > > to deal with. It's a 2500-line commit, plus the dependencies, which I > > don't think are accounted for here. (What's the baseline for the revert > > anyway?) I don't expect all the dependent commits to be easy to revert > > or backport to v6.1 or v6.2. > > > > *sad trombone* > > Yeah that's the other thing. 2500 patch revert is not cc stable material. > So this isn't even really helping users all that much. > > Unless it also comes with full amounts of backports of the reverts on all > affected drivers for all curent stable trees, fully validated. > > This is bad. I do think we need to have some understanding first of what > "fix this in amdgpu" would look like as plan B. Because plan A does not > look like a happy one at all. Yeah this whole thing has been a mess, I'm partially to blame here - we should have reverted earlier, but a lot of this has been me finding out that the problem here is a lot bigger then I previously imagined - and has not at all been easy to untangle. I've also dumped so much time into trying to figure it out that was more or less the only reason I acked this in the first place, I'm literally just quite tired and exhausted at this point from spinning my wheels on trying to fix this ;_;. I am sure there is a real proper fix for this, if anyone wants to help me try and figure this out I'm happy to setup remote access to the machines I've got here. I'll also try to push myself to dig further into this next week again. > -Daniel > > > BR, > > Jani. > > > > > > On Thu, 12 Jan 2023, Wayne Lin wrote: > > > This reverts commit 4d07b0bc403403438d9cf88450506240c5faf92f. > > > > > > [Why] > > > Changes cause regression on amdgpu mst. > > > E.g. > > > In fill_dc_mst_payload_table_from_drm(), amdgpu expects to add/remove > > > payload > > > one by one and call fill_dc_mst_payload_table_from_drm() to update the HW > > > maintained payload table. But previous change tries to go through all the > > > payloads in mst_state and update amdpug hw maintained table in once > > > everytime > > > driver only tries to add/remove a specific payload stream only. The newly > > > design idea conflicts with the implementation in amdgpu nowadays. > > > > > > [How] > > > Revert this patch first. After addressing all regression problems caused > > > by > > > this previous patch, will add it back and adjust it. > > > > > > Signed-off-by: Wayne Lin > > > Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2171 > > > Cc: sta...@vger.kernel.org # 6.1 > > > Cc: Lyude Paul > > > Cc: Harry Wentland > > > Cc: Mario Limonciello > > > Cc: Ville Syrjälä > > > Cc: Ben Skeggs > > > Cc: Stanislav Lisovskiy > > > Cc: Fangzhi Zuo > > > --- > > > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 53 +- > > > .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 106 ++- > > > .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 87 ++- > > > .../amd/display/include/link_service_types.h | 3 - > > > drivers/gpu/drm/display/drm_dp_mst_topology.c | 724 -- > > > drivers/gpu/drm/i915/display/intel_dp_mst.c | 67 +- > > > drivers/gpu/drm/i915/display/intel_hdcp.c | 24 +- > > > drivers/gpu/drm/nouveau/dispnv50/disp.c | 167 ++-- > > > include/drm/display/drm_dp_mst_helper.h | 177 +++-- > > > 9 files changed, 878 insertions(+)
Re: [Intel-gfx] [PATCH v2 1/9] drm/i915/dp_mst: Add the MST topology state for modesetted CRTCs
Hi! should have a chance to look at this at the start of next week On Thu, 2023-01-26 at 11:13 +0200, Imre Deak wrote: > Add the MST topology for a CRTC to the atomic state if the driver > needs to force a modeset on the CRTC after the encoder compute config > functions are called. > > Later the MST encoder's disable hook also adds the state, but that isn't > guaranteed to work (since in that hook getting the state may fail, which > can't be handled there). This should fix that, while a later patch fixes > the use of the MST state in the disable hook. > > v2: Add missing forward struct declartions, caught by hdrtest. > > Cc: Lyude Paul > Cc: sta...@vger.kernel.org # 6.1 > Signed-off-by: Imre Deak > --- > drivers/gpu/drm/i915/display/intel_display.c | 4 +++ > drivers/gpu/drm/i915/display/intel_dp_mst.c | 37 > drivers/gpu/drm/i915/display/intel_dp_mst.h | 4 +++ > 3 files changed, 45 insertions(+) > > diff --git a/drivers/gpu/drm/i915/display/intel_display.c > b/drivers/gpu/drm/i915/display/intel_display.c > index 717ca3d7890d3..d3994e2a7d636 100644 > --- a/drivers/gpu/drm/i915/display/intel_display.c > +++ b/drivers/gpu/drm/i915/display/intel_display.c > @@ -5934,6 +5934,10 @@ int intel_modeset_all_pipes(struct intel_atomic_state > *state, > if (ret) > return ret; > > + ret = intel_dp_mst_add_topology_state_for_crtc(state, crtc); > + if (ret) > + return ret; > + > ret = intel_atomic_add_affected_planes(state, crtc); > if (ret) > return ret; > diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c > b/drivers/gpu/drm/i915/display/intel_dp_mst.c > index 8b0e4defa3f10..ba29c294b7c1b 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c > +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c > @@ -1223,3 +1223,40 @@ bool intel_dp_mst_is_slave_trans(const struct > intel_crtc_state *crtc_state) > return crtc_state->mst_master_transcoder != INVALID_TRANSCODER && > crtc_state->mst_master_transcoder != crtc_state->cpu_transcoder; > } > + > +/** > + * intel_dp_mst_add_topology_state_for_crtc - add MST topology state for a > CRTC > + * @state: atomic state > + * @crtc: CRTC > + * > + * Add the MST topology state for @crtc to @state. > + * > + * Returns 0 on success, negative error code on failure. > + */ > +int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state > *state, > + struct intel_crtc *crtc) > +{ > + struct drm_connector *_connector; > + struct drm_connector_state *conn_state; > + int i; > + > + for_each_new_connector_in_state(&state->base, _connector, conn_state, > i) { > + struct drm_dp_mst_topology_state *mst_state; > + struct intel_connector *connector = > to_intel_connector(_connector); > + > + if (conn_state->crtc != &crtc->base) > + continue; > + > + if (!connector->mst_port) > + continue; > + > + mst_state = drm_atomic_get_mst_topology_state(&state->base, > + > &connector->mst_port->mst_mgr); > + if (IS_ERR(mst_state)) > + return PTR_ERR(mst_state); > + > + mst_state->pending_crtc_mask |= drm_crtc_mask(&crtc->base); > + } > + > + return 0; > +} > diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.h > b/drivers/gpu/drm/i915/display/intel_dp_mst.h > index f7301de6cdfb3..f1815bb722672 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp_mst.h > +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.h > @@ -8,6 +8,8 @@ > > #include > > +struct intel_atomic_state; > +struct intel_crtc; > struct intel_crtc_state; > struct intel_digital_port; > struct intel_dp; > @@ -18,5 +20,7 @@ int intel_dp_mst_encoder_active_links(struct > intel_digital_port *dig_port); > bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state); > bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state); > bool intel_dp_mst_source_support(struct intel_dp *intel_dp); > +int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state > *state, > + struct intel_crtc *crtc); > > #endif /* __INTEL_DP_MST_H__ */ -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat
Re: [Intel-gfx] [PATCH v2 3/3] drm: Call vga_switcheroo_process_delayed_switch() in drm_lastclose
Acked-by: Lyude Paul On Thu, 2023-01-12 at 21:11 +0100, Thomas Zimmermann wrote: > Several lastclose helpers call vga_switcheroo_process_delayed_switch(). > It's better to call the helper from drm_lastclose() after the kernel > client's screen has been restored. This way, all drivers can benefit > without having to implement their own lastclose helper. For drivers > without vga-switcheroo, vga_switcheroo_process_delayed_switch() does > nothing. > > There was an earlier patchset to do something similar. [1] > > v2: > * handle vga_switcheroo_client_fb_set() in a separate patch > * also update i915, nouveau and radeon > * remove unnecessary include statements > * update vga-switcheroo docs > > Suggested-by: Alexander Deucher > Signed-off-by: Thomas Zimmermann > Reviewed-by: Daniel Vetter > Reviewed-by: Alex Deucher > Link: > https://lore.kernel.org/amd-gfx/20221020143603.563929-1-alexander.deuc...@amd.com/ > # 1 > --- > drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 - > drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 2 -- > drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 13 - > drivers/gpu/drm/drm_file.c | 3 +++ > drivers/gpu/drm/i915/i915_driver.c | 25 ++--- > drivers/gpu/drm/nouveau/nouveau_drm.c | 1 - > drivers/gpu/drm/nouveau/nouveau_vga.c | 7 --- > drivers/gpu/drm/nouveau/nouveau_vga.h | 1 - > drivers/gpu/drm/radeon/radeon_drv.c | 2 +- > drivers/gpu/drm/radeon/radeon_drv.h | 1 - > drivers/gpu/drm/radeon/radeon_kms.c | 18 -- > drivers/gpu/vga/vga_switcheroo.c| 4 ++-- > 12 files changed, 8 insertions(+), 70 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h > b/drivers/gpu/drm/amd/amdgpu/amdgpu.h > index 63c921c55fb9..7120b9b6e580 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h > @@ -1330,7 +1330,6 @@ extern const int amdgpu_max_kms_ioctl; > > int amdgpu_driver_load_kms(struct amdgpu_device *adev, unsigned long flags); > void amdgpu_driver_unload_kms(struct drm_device *dev); > -void amdgpu_driver_lastclose_kms(struct drm_device *dev); > int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file > *file_priv); > void amdgpu_driver_postclose_kms(struct drm_device *dev, >struct drm_file *file_priv); > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c > index 1353ffd08988..783c1e284a22 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c > @@ -34,7 +34,6 @@ > #include > #include > #include > -#include > #include > #include > #include > @@ -2785,7 +2784,6 @@ static const struct drm_driver amdgpu_kms_driver = { > DRIVER_SYNCOBJ_TIMELINE, > .open = amdgpu_driver_open_kms, > .postclose = amdgpu_driver_postclose_kms, > - .lastclose = amdgpu_driver_lastclose_kms, > .ioctls = amdgpu_ioctls_kms, > .num_ioctls = ARRAY_SIZE(amdgpu_ioctls_kms), > .dumb_create = amdgpu_mode_dumb_create, > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c > index 7aa7e52ca784..a37be02fb2fc 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c > @@ -34,7 +34,6 @@ > #include "amdgpu_vce.h" > #include "atom.h" > > -#include > #include > #include > #include > @@ -1104,18 +1103,6 @@ int amdgpu_info_ioctl(struct drm_device *dev, void > *data, struct drm_file *filp) > /* > * Outdated mess for old drm with Xorg being in charge (void function now). > */ > -/** > - * amdgpu_driver_lastclose_kms - drm callback for last close > - * > - * @dev: drm dev pointer > - * > - * Switch vga_switcheroo state after last close (all asics). > - */ > -void amdgpu_driver_lastclose_kms(struct drm_device *dev) > -{ > - drm_fb_helper_lastclose(dev); > - vga_switcheroo_process_delayed_switch(); > -} > > /** > * amdgpu_driver_open_kms - drm callback for open > diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c > index a51ff8cee049..314c309db9a3 100644 > --- a/drivers/gpu/drm/drm_file.c > +++ b/drivers/gpu/drm/drm_file.c > @@ -38,6 +38,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -460,6 +461,8 @@ void drm_lastclose(struct drm_device * dev) > drm_legacy_dev_reinit(dev); > > drm_client_dev_restore(dev); > + > + vga_switcheroo_process_delayed_switch(); > } > >
Re: [Intel-gfx] [PATCH v2 16/17] drm/i915/dp_mst: Add workaround for a DELL P2715Q payload allocation problem
On Tue, 2023-01-31 at 17:05 +0200, Imre Deak wrote: > The DELL P2715Q monitor's MST hub has a payload allocation problem, LMAO hello yet again, Dell P2715Q. It's been a while. > where the VCPI used to reserve the last two time slots (at position > 0x3e, 0x3f) in the hub's payload table, this VCPI can't be reused for > later payload configurations. > > The problem results at least in streams reusing older VCPIs to stay > blank on the screen and the payload table containing bogus VCPIs > (repeating the one earlier used to reserve the 0x3e, 0x3f slots) after > the last reservered slot. WOW. you know I've been trying for ages to figure out what is up with this exact monitor and I think I just gave up because it's the only monitor I've ever seen do this. (Also yes, I do have two of this exact monitor. I think we even have this model mentioned in our testcases. I looked up a google photo of it just to confirm. I think ours is the P2715Qb, but it looks identical and the problem you're describing sounds identical as well). This patch looks fine to me, we could probably also put this in the MST helpers as well if you can think of a way to do that and I can handle testing it on nouveau/amdgpu, but this is basically the only monitor I've ever seen do this - so I don't think it's a big deal either way. either way: Reviewed-by: Lyude Paul > > To work around the problem detect this monitor and the condition for the > problem (when the last two slots get allocated in a commit), force a > full modeset of the MST topology in the subsequent commit and reset the > payload table during the latter commit after all payloads have been > freed. > > Cc: Ville Syrjälä > Cc: Lyude Paul > Signed-off-by: Imre Deak > --- > drivers/gpu/drm/i915/display/intel_atomic.c | 13 +++-- > drivers/gpu/drm/i915/display/intel_atomic.h | 3 +- > .../drm/i915/display/intel_display_types.h| 1 + > drivers/gpu/drm/i915/display/intel_dp.c | 5 +- > drivers/gpu/drm/i915/display/intel_dp_mst.c | 48 +-- > 5 files changed, 61 insertions(+), 9 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c > b/drivers/gpu/drm/i915/display/intel_atomic.c > index 0f1c5b9c9a826..04e5f0e0fffa6 100644 > --- a/drivers/gpu/drm/i915/display/intel_atomic.c > +++ b/drivers/gpu/drm/i915/display/intel_atomic.c > @@ -616,7 +616,8 @@ intel_atomic_get_crtc_state(struct drm_atomic_state > *state, > } > > static int modeset_pipe(struct intel_atomic_state *state, > - struct intel_crtc *crtc, const char *reason) > + struct intel_crtc *crtc, const char *reason, > + bool allow_fastset) > { > struct drm_i915_private *i915 = to_i915(state->base.dev); > struct intel_crtc_state *crtc_state; > @@ -629,6 +630,8 @@ static int modeset_pipe(struct intel_atomic_state *state, > return PTR_ERR(crtc_state); > > crtc_state->uapi.mode_changed = true; > + if (!allow_fastset) > + crtc_state->uapi.connectors_changed = true; > crtc_state->update_pipe = false; > > return intel_atomic_add_affected_planes(state, crtc); > @@ -639,6 +642,7 @@ static int modeset_pipe(struct intel_atomic_state *state, > * @state: atomic state > * @connector: connector to add the state for > * @reason: the reason why the connector needs to be added > + * @allow_fastset: allow a fastset > * > * Add the @connector to the atomic state with its CRTC state and force a > modeset > * on the CRTC if any. > @@ -648,7 +652,8 @@ static int modeset_pipe(struct intel_atomic_state *state, > * Returns 0 in case of success, a negative error code on failure. > */ > int intel_atomic_modeset_connector(struct intel_atomic_state *state, > -struct intel_connector *connector, const > char *reason) > +struct intel_connector *connector, const > char *reason, > +bool allow_fastset) > { > struct drm_i915_private *i915 = to_i915(state->base.dev); > struct drm_connector_state *conn_state; > @@ -671,7 +676,7 @@ int intel_atomic_modeset_connector(struct > intel_atomic_state *state, > if (ret) > return ret; > > - return modeset_pipe(state, crtc, reason); > + return modeset_pipe(state, crtc, reason, allow_fastset); > } > > /** > @@ -700,7 +705,7 @@ int intel_atomic_modeset_pipe(struct intel_atomic_state > *state, > if (ret) > return ret; > > - return modeset_pipe(state, crtc, reason); > + return modeset_pipe(state, crtc, reas
Re: [Intel-gfx] [PATCH v2 02/17] drm/display/dp_mst: Handle old/new payload states in drm_dp_remove_payload()
On Tue, 2023-01-31 at 17:05 +0200, Imre Deak wrote: > Atm, drm_dp_remove_payload() uses the same payload state to both get the > vc_start_slot required for the payload removal DPCD message and to > deduct time_slots from vc_start_slot of all payloads after the one being > removed. > > The above isn't always correct, as vc_start_slot must be the up-to-date > version contained in the new payload state, but time_slots must be the > one used when the payload was previously added, contained in the old > payload state. The new payload's time_slots can change vs. the old one > if the current atomic commit changes the corresponding mode. > > This patch let's drivers pass the old and new payload states to > drm_dp_remove_payload(), but keeps these the same for now in all drivers > not to change the behavior. A follow-up i915 patch will pass in that > driver the correct old and new states to the function. Oh wow, this was definitely a mistake on my part, thanks for catching this! TBH, I think this behavior is correct so (now that I actually have a setup capable of testing amdgpu's MST fully thanks to gitlab issue 2171…) if you'd like to change it on other drivers as well I can test it fully. Or feel free to leave it to me, shouldn't be too difficult I think :). For 0-2: Reviewed-by: Lyude Paul > > Cc: Lyude Paul > Cc: Ville Syrjälä > Cc: Ben Skeggs > Cc: Karol Herbst > Cc: Harry Wentland > Cc: Alex Deucher > Cc: Wayne Lin > Cc: sta...@vger.kernel.org # 6.1 > Cc: dri-de...@lists.freedesktop.org > Reviewed-by: Ville Syrjälä > Signed-off-by: Imre Deak > --- > .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 2 +- > drivers/gpu/drm/display/drm_dp_mst_topology.c | 26 ++- > drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++- > drivers/gpu/drm/nouveau/dispnv50/disp.c | 2 +- > include/drm/display/drm_dp_mst_helper.h | 3 ++- > 5 files changed, 21 insertions(+), 16 deletions(-) > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > index a50319fc42b11..180d3893b68da 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > @@ -208,7 +208,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( > if (enable) > drm_dp_add_payload_part1(mst_mgr, mst_state, payload); > else > - drm_dp_remove_payload(mst_mgr, mst_state, payload); > + drm_dp_remove_payload(mst_mgr, mst_state, payload, payload); > > /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or >* AUX message. The sequence is slot 1-63 allocated sequence for each > diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c > b/drivers/gpu/drm/display/drm_dp_mst_topology.c > index 847c10aa2098c..1990ff5dc7ddd 100644 > --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c > +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c > @@ -3342,7 +3342,8 @@ EXPORT_SYMBOL(drm_dp_add_payload_part1); > * drm_dp_remove_payload() - Remove an MST payload > * @mgr: Manager to use. > * @mst_state: The MST atomic state > - * @payload: The payload to write > + * @old_payload: The payload with its old state > + * @new_payload: The payload to write > * > * Removes a payload from an MST topology if it was successfully assigned a > start slot. Also updates > * the starting time slots of all other payloads which would have been > shifted towards the start of > @@ -3350,36 +3351,37 @@ EXPORT_SYMBOL(drm_dp_add_payload_part1); > */ > void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, > struct drm_dp_mst_topology_state *mst_state, > -struct drm_dp_mst_atomic_payload *payload) > +const struct drm_dp_mst_atomic_payload *old_payload, > +struct drm_dp_mst_atomic_payload *new_payload) > { > struct drm_dp_mst_atomic_payload *pos; > bool send_remove = false; > > /* We failed to make the payload, so nothing to do */ > - if (payload->vc_start_slot == -1) > + if (new_payload->vc_start_slot == -1) > return; > > mutex_lock(&mgr->lock); > - send_remove = drm_dp_mst_port_downstream_of_branch(payload->port, > mgr->mst_primary); > + send_remove = drm_dp_mst_port_downstream_of_branch(new_payload->port, > mgr->mst_primary); > mutex_unlock(&mgr->lock); > > if (send_remove) > - drm_dp_destroy_payload_step1(mgr, mst_state, payload); > + drm_dp_destroy_payload_
Re: [Intel-gfx] [PATCH v2 06/17] drm/display/dp_mst: Sanitize payload iteration in drm_dp_mst_dump_topology()
On Tue, 2023-01-31 at 17:05 +0200, Imre Deak wrote: > Simplify the loop iterating the payloads by using a helper to get a > payload by its VCPI (keeping the list VCPI sorted). This also removes > the assumption that the biggest VCPI matches the number of payloads > (even though this holds now). > > Suggested-by: Ville Syrjälä > Cc: Lyude Paul > Cc: Ville Syrjälä > Signed-off-by: Imre Deak > --- > drivers/gpu/drm/display/drm_dp_mst_topology.c | 45 --- > 1 file changed, 28 insertions(+), 17 deletions(-) > > diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c > b/drivers/gpu/drm/display/drm_dp_mst_topology.c > index 8787df19f428b..0c04b96ae614c 100644 > --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c > +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c > @@ -4737,6 +4737,18 @@ static void fetch_monitor_name(struct > drm_dp_mst_topology_mgr *mgr, > kfree(mst_edid); > } > > +static struct drm_dp_mst_atomic_payload * > +get_payload_by_vcpi(struct drm_dp_mst_topology_state *mst_state, int vcpi) > +{ > + struct drm_dp_mst_atomic_payload *payload; > + > + list_for_each_entry(payload, &mst_state->payloads, next) > + if (payload->vcpi == vcpi) > + return payload; > + > + return NULL; > +} > + > /** > * drm_dp_mst_dump_topology(): dump topology to seq file. > * @m: seq_file to dump output to > @@ -4748,7 +4760,6 @@ void drm_dp_mst_dump_topology(struct seq_file *m, > struct drm_dp_mst_topology_mgr *mgr) > { > struct drm_dp_mst_topology_state *state; > - struct drm_dp_mst_atomic_payload *payload; > int i, ret; > > mutex_lock(&mgr->lock); > @@ -4768,24 +4779,24 @@ void drm_dp_mst_dump_topology(struct seq_file *m, > state->payload_mask, mgr->max_payloads, state->start_slot, > state->pbn_div); > > seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | sink > name |\n"); > - for (i = 0; i < mgr->max_payloads; i++) { > - list_for_each_entry(payload, &state->payloads, next) { > - char name[14]; > + for_each_set_bit(i, (unsigned long *)&state->payload_mask, > + BITS_PER_TYPE(state->payload_mask)) { Huh, TIL we have a for_each_set_bit() macro. neat. > + const struct drm_dp_mst_atomic_payload *payload = > get_payload_by_vcpi(state, i + 1); > + char name[14]; > > - if (payload->vcpi != i + 1 || payload->delete) > - continue; > + if (payload->delete) > + continue; > > - fetch_monitor_name(mgr, payload->port, name, > sizeof(name)); > - seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %19s\n", > -i, > -payload->port->port_num, > -payload->vcpi, > -payload->vc_start_slot, > -payload->vc_start_slot + payload->time_slots > - 1, > -payload->pbn, > -payload->dsc_enabled ? "Y" : "N", > -(*name != 0) ? name : "Unknown"); > - } > + fetch_monitor_name(mgr, payload->port, name, sizeof(name)); > + seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %19s\n", > +i, > +payload->port->port_num, > + payload->vcpi, > +payload->vc_start_slot, > +payload->vc_start_slot + payload->time_slots - 1, > +payload->pbn, > +payload->dsc_enabled ? "Y" : "N", > +(*name != 0) ? name : "Unknown"); > } > > seq_printf(m, "\n*** DPCD Info ***\n"); -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat
Re: [Intel-gfx] [PATCH v2 16/17] drm/i915/dp_mst: Add workaround for a DELL P2715Q payload allocation problem
I mentioned this once already but: really, I am genuinely impressed at this! I never expected to see this monitor ever work. Also, thank you a ton for adding the payload table verification stuff here. For the whole series: Reviewed-by: Lyude Paul On Tue, 2023-01-31 at 17:05 +0200, Imre Deak wrote: > The DELL P2715Q monitor's MST hub has a payload allocation problem, > where the VCPI used to reserve the last two time slots (at position > 0x3e, 0x3f) in the hub's payload table, this VCPI can't be reused for > later payload configurations. > > The problem results at least in streams reusing older VCPIs to stay > blank on the screen and the payload table containing bogus VCPIs > (repeating the one earlier used to reserve the 0x3e, 0x3f slots) after > the last reservered slot. > > To work around the problem detect this monitor and the condition for the > problem (when the last two slots get allocated in a commit), force a > full modeset of the MST topology in the subsequent commit and reset the > payload table during the latter commit after all payloads have been > freed. > > Cc: Ville Syrjälä > Cc: Lyude Paul > Signed-off-by: Imre Deak > --- > drivers/gpu/drm/i915/display/intel_atomic.c | 13 +++-- > drivers/gpu/drm/i915/display/intel_atomic.h | 3 +- > .../drm/i915/display/intel_display_types.h| 1 + > drivers/gpu/drm/i915/display/intel_dp.c | 5 +- > drivers/gpu/drm/i915/display/intel_dp_mst.c | 48 +-- > 5 files changed, 61 insertions(+), 9 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c > b/drivers/gpu/drm/i915/display/intel_atomic.c > index 0f1c5b9c9a826..04e5f0e0fffa6 100644 > --- a/drivers/gpu/drm/i915/display/intel_atomic.c > +++ b/drivers/gpu/drm/i915/display/intel_atomic.c > @@ -616,7 +616,8 @@ intel_atomic_get_crtc_state(struct drm_atomic_state > *state, > } > > static int modeset_pipe(struct intel_atomic_state *state, > - struct intel_crtc *crtc, const char *reason) > + struct intel_crtc *crtc, const char *reason, > + bool allow_fastset) > { > struct drm_i915_private *i915 = to_i915(state->base.dev); > struct intel_crtc_state *crtc_state; > @@ -629,6 +630,8 @@ static int modeset_pipe(struct intel_atomic_state *state, > return PTR_ERR(crtc_state); > > crtc_state->uapi.mode_changed = true; > + if (!allow_fastset) > + crtc_state->uapi.connectors_changed = true; > crtc_state->update_pipe = false; > > return intel_atomic_add_affected_planes(state, crtc); > @@ -639,6 +642,7 @@ static int modeset_pipe(struct intel_atomic_state *state, > * @state: atomic state > * @connector: connector to add the state for > * @reason: the reason why the connector needs to be added > + * @allow_fastset: allow a fastset > * > * Add the @connector to the atomic state with its CRTC state and force a > modeset > * on the CRTC if any. > @@ -648,7 +652,8 @@ static int modeset_pipe(struct intel_atomic_state *state, > * Returns 0 in case of success, a negative error code on failure. > */ > int intel_atomic_modeset_connector(struct intel_atomic_state *state, > -struct intel_connector *connector, const > char *reason) > +struct intel_connector *connector, const > char *reason, > +bool allow_fastset) > { > struct drm_i915_private *i915 = to_i915(state->base.dev); > struct drm_connector_state *conn_state; > @@ -671,7 +676,7 @@ int intel_atomic_modeset_connector(struct > intel_atomic_state *state, > if (ret) > return ret; > > - return modeset_pipe(state, crtc, reason); > + return modeset_pipe(state, crtc, reason, allow_fastset); > } > > /** > @@ -700,7 +705,7 @@ int intel_atomic_modeset_pipe(struct intel_atomic_state > *state, > if (ret) > return ret; > > - return modeset_pipe(state, crtc, reason); > + return modeset_pipe(state, crtc, reason, true); > } > > /** > diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h > b/drivers/gpu/drm/i915/display/intel_atomic.h > index 84295d388e3cb..7778aea8a09fe 100644 > --- a/drivers/gpu/drm/i915/display/intel_atomic.h > +++ b/drivers/gpu/drm/i915/display/intel_atomic.h > @@ -59,7 +59,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private > *dev_priv, > int intel_atomic_modeset_pipe(struct intel_atomic_state *state, > struct intel_crtc *crtc, const char *reason); > int intel_atomic_modeset_connector(struct i
Re: [Intel-gfx] [PATCH v2 02/17] drm/display/dp_mst: Handle old/new payload states in drm_dp_remove_payload()
On Wed, 2023-02-01 at 17:04 +0200, Imre Deak wrote: > > Yes, this patch should have no functional change, so please check what > would apply to other drivers as well. > > Could you also check Ville's comment about storing start_slot elsewhere > than the atomic state (leaving only time_slots there). I wonder if that > would work, at least it would simplify things I think. Ideally it'd be nice if we could have all this info in the atomic commit, but yeah - you're not the first person to find this a bit confusing. FWIW though, the way we store start_slot right now is intended to follow the same kind of behavior we have with atomic CRTC commit dependencies, I think I also made it that way so all of the state would be in a single place but there's no hard requirement for it. So if you guys think it'd be better design-wise to store this something else, I've got no strong feelings either way > > > For 0-2: > > > > Reviewed-by: Lyude Paul > > Thanks. > > > > > > > > > Cc: Lyude Paul > > > Cc: Ville Syrjälä > > > Cc: Ben Skeggs > > > Cc: Karol Herbst > > > Cc: Harry Wentland > > > Cc: Alex Deucher > > > Cc: Wayne Lin > > > Cc: sta...@vger.kernel.org # 6.1 > > > Cc: dri-de...@lists.freedesktop.org > > > Reviewed-by: Ville Syrjälä > > > Signed-off-by: Imre Deak > > > --- > > > .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 2 +- > > > drivers/gpu/drm/display/drm_dp_mst_topology.c | 26 ++- > > > drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++- > > > drivers/gpu/drm/nouveau/dispnv50/disp.c | 2 +- > > > include/drm/display/drm_dp_mst_helper.h | 3 ++- > > > 5 files changed, 21 insertions(+), 16 deletions(-) > > > > > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > > > b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > > > index a50319fc42b11..180d3893b68da 100644 > > > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > > > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c > > > @@ -208,7 +208,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( > > > if (enable) > > > drm_dp_add_payload_part1(mst_mgr, mst_state, payload); > > > else > > > - drm_dp_remove_payload(mst_mgr, mst_state, payload); > > > + drm_dp_remove_payload(mst_mgr, mst_state, payload, payload); > > > > > > /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or > > >* AUX message. The sequence is slot 1-63 allocated sequence for each > > > diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c > > > b/drivers/gpu/drm/display/drm_dp_mst_topology.c > > > index 847c10aa2098c..1990ff5dc7ddd 100644 > > > --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c > > > +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c > > > @@ -3342,7 +3342,8 @@ EXPORT_SYMBOL(drm_dp_add_payload_part1); > > > * drm_dp_remove_payload() - Remove an MST payload > > > * @mgr: Manager to use. > > > * @mst_state: The MST atomic state > > > - * @payload: The payload to write > > > + * @old_payload: The payload with its old state > > > + * @new_payload: The payload to write > > > * > > > * Removes a payload from an MST topology if it was successfully > > > assigned a start slot. Also updates > > > * the starting time slots of all other payloads which would have been > > > shifted towards the start of > > > @@ -3350,36 +3351,37 @@ EXPORT_SYMBOL(drm_dp_add_payload_part1); > > > */ > > > void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, > > > struct drm_dp_mst_topology_state *mst_state, > > > -struct drm_dp_mst_atomic_payload *payload) > > > +const struct drm_dp_mst_atomic_payload *old_payload, > > > +struct drm_dp_mst_atomic_payload *new_payload) > > > { > > > struct drm_dp_mst_atomic_payload *pos; > > > bool send_remove = false; > > > > > > /* We failed to make the payload, so nothing to do */ > > > - if (payload->vc_start_slot == -1) > > > + if (new_payload->vc_start_slot == -1) > > > return; > > > > > > mutex_lock(&mgr->lock); > > > - send_remove = drm_dp_mst_port_downstream_of_branch(payload->port, > > > mgr->mst_primary); > > &g
Re: [Intel-gfx] [PATCH v2 02/17] drm/display/dp_mst: Handle old/new payload states in drm_dp_remove_payload()
On Tue, 2023-02-07 at 14:11 +0200, Imre Deak wrote: > On Mon, Feb 06, 2023 at 07:42:32PM -0500, Lyude Paul wrote: > > On Wed, 2023-02-01 at 17:04 +0200, Imre Deak wrote: > > > > > > Yes, this patch should have no functional change, so please check what > > > would apply to other drivers as well. > > > > > > Could you also check Ville's comment about storing start_slot elsewhere > > > than the atomic state (leaving only time_slots there). I wonder if that > > > would work, at least it would simplify things I think. > > > > Ideally it'd be nice if we could have all this info in the atomic commit, > > but > > yeah - you're not the first person to find this a bit confusing. FWIW > > though, > > the way we store start_slot right now is intended to follow the same kind of > > behavior we have with atomic CRTC commit dependencies, I think I also made > > it > > that way so all of the state would be in a single place but there's no hard > > requirement for it. > > As I understood the atomic state should be precomputed during atomic > check and not changed later during the commit phase. In case of > start_slot this would mean knowing in advance the actual (driver > specific) disabling / enabling sequence of the payloads, not sure how > feasible it is to figure that out. start_slot also changes dynamically It isn't feasible afaict, which was the main motivation for having the strange update procedure - this is pretty much something that needs to be determined at commit time. > as each payload is disabled, so these intermediate versions of the field > would need to be tracked somehow separately from the final (precomputed) > versions. FWIW, the current way this works is that we call drm_dp_mst_atomic_wait_for_dependencies() in order to ensure that no one's currently writing to the start_slot field (remember, at this point we may not have atomic locks held anymore). Then at that point, start_slot in the new atomic state should hold the current in-hw start_slot value. start_slot isn't actually set to the new starting slot until drm_dp_add_payload_part1(). That of course as you pointed out, doesn't help us unless we read all of the start_slot values before we start changing payloads - since disabling payloads inevitably changes the start slot of payloads that come afterwards. I'll admit I'm a bit surprised this logic was wrong - if I recall the whole reason I assumed this was OK when writing this code was that since we're updating one payload at a time, ACT, repeat, that the start slots of each payload in hardware _would_ actually change in the same way we modify them in helpers. e.g., my expectation was if we had a bunch of payloads like this: Payload #1: 15 slots, start_slot=0 Payload #2: 15 slots, start_slot=15 Payload #3: 15 slots, start_slot=30 And then disabled say, payload #1, that immediately after we get the ACT that the payload table in hardware would look like this: Payload #2: 15 slots, start_slot=0 Payload #3: 15 slots, start_slot=15 But it sounds like it actually would look like this in the real world? Payload #2: 15 slots, start_slot=15 Payload #3: 15 slots, start_slot=30 So I'm curious, is there something I missed here? At what point does the MST hub at the other end decide that it's time to move the start slots back? (keep in mind, the MST specification does explicitly mention that there should never be holes in the payload table - so something has to be shifting the payloads back). > > > So if you guys think it'd be better design-wise to store this something > > else, > > I've got no strong feelings either way > > > > > > > For 0-2: > > > > > > > > Reviewed-by: Lyude Paul > > > > > > Thanks. > > > > > > > > > > > > > > > > > Cc: Lyude Paul > > > > > Cc: Ville Syrjälä > > > > > Cc: Ben Skeggs > > > > > Cc: Karol Herbst > > > > > Cc: Harry Wentland > > > > > Cc: Alex Deucher > > > > > Cc: Wayne Lin > > > > > Cc: sta...@vger.kernel.org # 6.1 > > > > > Cc: dri-de...@lists.freedesktop.org > > > > > Reviewed-by: Ville Syrjälä > > > > > Signed-off-by: Imre Deak > > > > > --- > > > > > .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 2 +- > > > > > drivers/gpu/drm/display/drm_dp_mst_topology.c | 26 > > > > > ++- > > > > > drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++- > > > > > drivers/gpu/drm/nouveau/dispnv50/disp.c | 2 +- > > >
Re: [Intel-gfx] [PATCH v2 02/17] drm/display/dp_mst: Handle old/new payload states in drm_dp_remove_payload()
On Wed, 2023-02-08 at 09:41 +0200, Imre Deak wrote: > On Tue, Feb 07, 2023 at 07:21:48PM -0500, Lyude Paul wrote: > > On Tue, 2023-02-07 at 14:11 +0200, Imre Deak wrote: > > > > And then disabled say, payload #1, that immediately after we get the ACT > > that > > the payload table in hardware would look like this: > > > > Payload #2: 15 slots, start_slot=0 > > Payload #3: 15 slots, start_slot=15 > > The above is the actual and expected HW state state yes. > > > But it sounds like it actually would look like this in the real world? > > > > Payload #2: 15 slots, start_slot=15 > > Payload #3: 15 slots, start_slot=30 > > No, the problem currently is not that start_slot of the subsequent > payloads are not shifted towards the beginning. Rather the atomic state > doesn't get updated properly, becoming out of sync with the HW. For > instance in a commit resizing payload #1, in the commit phase > (intel_atomic_commit_tail()) will begin by removing payload #1. The > initial state is > > old payload state new payload state > Payload #1: 15 slots, start_slot=020 slots, start_slot=0 > Payload #2: 15 slots, start_slot=15 15 slots, start_slot=15 > Payload #3: 15 slots, start_slot=30 15 slots, start_slot=30 > > mgr->next_start_slot = 45 > > intel_mst_disable_dp() will pass the old MST and payload state to > drm_dp_remove_payload(): The MST state was added during atomic check, > since payload #1 changed, then intel_atomic_commit() -> > drm_atomic_helper_swap_state() sets the MST current state (returned by > drm_atomic_get_mst_topology_state()) to point to the old state. So at OK - this took me a while to wrap my head around but you're completely right. It appears I totally misunderstood where the state swapping actually happens during the check -> commit sequence. I think if that's how things work too then yeah, it definitely might not be a bad idea to move the start slot out of the atomic state :P. I guess we could just keep this in the mst manager struct instead of the commit state and make the rules for access be the same: protect them through commit ordering, and document that the proper way of accessing start values outside of the context of an atomic commit (if this was needed for some reason) is: * grab mst lock * call drm_dp_mst_atomic_wait_for_dependencies() * read values under lock Thank y'all again so much for helping out with this! It is super appreciated, and once you guys push these patches upstream I will look into adopting this for nouveau. I already poked some folks from AMD as well to make sure they're keeping an eye on this (although looking at the Cc I realize they were already added a while ago, whoops lol). > the point drm_dp_remove_payload() returns we have: > > old payload state new payload state > Payload #1: 15 slots, start_slot=-1 20 slots, start_slot=0 > Payload #2: 15 slots, start_slot=015 slots, start_slot=15 > Payload #3: 15 slots, start_slot=15 15 slots, start_slot=30 > > mgr->next_start_slot = 30 > > then after re-enabling payload #1, after drm_dp_add_payload_part1() > returns (passing to it the new MST and payload state) we have: > > old payload state new payload state > Payload #1: 15 slots, start_slot=-1 20 slots, start_slot=30 > Payload #2: 15 slots, start_slot=015 slots, start_slot=15 > Payload #3: 15 slots, start_slot=15 15 slots, start_slot=30 > > mgr->next_start_slot = 50 > > So in the new SW state payload #1 and #3 incorrectly overlap, with the > actual HW state being: > > Payload #1: 20 slots, start_slot=30 > Payload #2: 15 slots, start_slot=0 > Payload #3: 15 slots, start_slot=15 > > A subsequent commit will see the wrong start_slot in the SW state for > payload #2 (15) and #3 (30). > > > So I'm curious, is there something I missed here? At what point does the MST > > hub at the other end decide that it's time to move the start slots back? > > The hub shifts back payloads after the DPCD write command to 0x1c0 - > 0x1c2 to remove a payload. (The HW OTOH does the corresponding shift at > the point of disabling the stream, in intel_mst_post_disable_dp() -> > intel_disable_transcoder() for i915). > > > (keep in mind, the MST specification does explicitly mention that > > there should never be holes in the payload table - so something has to > > be shifting the payloads back). > > Right, the hubs I checked conform to this. > > > > > So if you guys think it'd be better design-wise to store this something > > > > else, > > > > I've got no strong feelings either way > > > > >
[Intel-gfx] [Cc: drm-misc folks] Re: [CI 1/4] drm/i915/dp_mst: Add the MST topology state for modesetted CRTCs
On Tue, 2023-02-07 at 14:59 +0200, Imre Deak wrote: > Hi all, > > On Mon, Feb 06, 2023 at 01:48:53PM +0200, Imre Deak wrote: > > Add the MST topology for a CRTC to the atomic state if the driver > > needs to force a modeset on the CRTC after the encoder compute config > > functions are called. > > > > Later the MST encoder's disable hook also adds the state, but that isn't > > guaranteed to work (since in that hook getting the state may fail, which > > can't be handled there). This should fix that, while a later patch fixes > > the use of the MST state in the disable hook. > > > > v2: Add missing forward struct declartions, caught by hdrtest. > > v3: Factor out intel_dp_mst_add_topology_state_for_connector() used > > later in the patchset. > > > > Cc: Lyude Paul > > Cc: Ville Syrjälä > > Cc: sta...@vger.kernel.org # 6.1 > > Reviewed-by: Ville Syrjälä # v2 > > Reviewed-by: Lyude Paul > > Signed-off-by: Imre Deak > > Is it ok to merge these 4 patches (also at [1]), via the i915 tree? > > If so could it be also acked from the AMD and Nouveau side? Whichever branch works best for y'all is fine by me, if it's via i915's tree I guess we might need to back-merge drm-misc at some point so I can write up equivalent fixes for nouveau as well. (Added Thomas Zimmermann to Cc) > > [1] https://patchwork.freedesktop.org/series/113703/ > > > --- > > drivers/gpu/drm/i915/display/intel_display.c | 4 ++ > > drivers/gpu/drm/i915/display/intel_dp_mst.c | 61 > > drivers/gpu/drm/i915/display/intel_dp_mst.h | 4 ++ > > 3 files changed, 69 insertions(+) > > > > diff --git a/drivers/gpu/drm/i915/display/intel_display.c > > b/drivers/gpu/drm/i915/display/intel_display.c > > index 12ade593c..38106cf63b3b9 100644 > > --- a/drivers/gpu/drm/i915/display/intel_display.c > > +++ b/drivers/gpu/drm/i915/display/intel_display.c > > @@ -5936,6 +5936,10 @@ int intel_modeset_all_pipes(struct > > intel_atomic_state *state, > > if (ret) > > return ret; > > > > + ret = intel_dp_mst_add_topology_state_for_crtc(state, crtc); > > + if (ret) > > + return ret; > > + > > ret = intel_atomic_add_affected_planes(state, crtc); > > if (ret) > > return ret; > > diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c > > b/drivers/gpu/drm/i915/display/intel_dp_mst.c > > index 8b0e4defa3f10..f3cb12dcfe0a7 100644 > > --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c > > +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c > > @@ -1223,3 +1223,64 @@ bool intel_dp_mst_is_slave_trans(const struct > > intel_crtc_state *crtc_state) > > return crtc_state->mst_master_transcoder != INVALID_TRANSCODER && > >crtc_state->mst_master_transcoder != crtc_state->cpu_transcoder; > > } > > + > > +/** > > + * intel_dp_mst_add_topology_state_for_connector - add MST topology state > > for a connector > > + * @state: atomic state > > + * @connector: connector to add the state for > > + * @crtc: the CRTC @connector is attached to > > + * > > + * Add the MST topology state for @connector to @state. > > + * > > + * Returns 0 on success, negative error code on failure. > > + */ > > +static int > > +intel_dp_mst_add_topology_state_for_connector(struct intel_atomic_state > > *state, > > + struct intel_connector *connector, > > + struct intel_crtc *crtc) > > +{ > > + struct drm_dp_mst_topology_state *mst_state; > > + > > + if (!connector->mst_port) > > + return 0; > > + > > + mst_state = drm_atomic_get_mst_topology_state(&state->base, > > + > > &connector->mst_port->mst_mgr); > > + if (IS_ERR(mst_state)) > > + return PTR_ERR(mst_state); > > + > > + mst_state->pending_crtc_mask |= drm_crtc_mask(&crtc->base); > > + > > + return 0; > > +} > > + > > +/** > > + * intel_dp_mst_add_topology_state_for_crtc - add MST topology state for a > > CRTC > > + * @state: atomic state > > + * @crtc: CRTC to add the state for > > + * > > + * Add the MST topology state for @crtc to @state. > > + * > > + * Returns 0 on success, negative error code on failure. > > + */ > > +int i
Re: [Intel-gfx] [PATCH 1/3] drm/display/dp_mst: Fix down/up message handling after sink disconnect
For the whole series: Reviewed-by: Lyude Paul Thanks! On Wed, 2022-12-14 at 20:42 +0200, Imre Deak wrote: > If the sink gets disconnected during receiving a multi-packet DP MST AUX > down-reply/up-request sideband message, the state keeping track of which > packets have been received already is not reset. This results in a failed > sanity check for the subsequent message packet received after a sink is > reconnected (due to the pending message not yet completed with an > end-of-message-transfer packet), indicated by the > > "sideband msg set header failed" > > error. > > Fix the above by resetting the up/down message reception state after a > disconnect event. > > Cc: Lyude Paul > Cc: # v3.17+ > Signed-off-by: Imre Deak > --- > drivers/gpu/drm/display/drm_dp_mst_topology.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c > b/drivers/gpu/drm/display/drm_dp_mst_topology.c > index 51a46689cda70..90819fff2c9ba 100644 > --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c > +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c > @@ -3641,6 +3641,9 @@ int drm_dp_mst_topology_mgr_set_mst(struct > drm_dp_mst_topology_mgr *mgr, bool ms > drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); > ret = 0; > mgr->payload_id_table_cleared = false; > + > + memset(&mgr->down_rep_recv, 0, sizeof(mgr->down_rep_recv)); > + memset(&mgr->up_req_recv, 0, sizeof(mgr->up_req_recv)); > } > > out_unlock: -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat
Re: [Intel-gfx] 5.6 DP-MST regression: 1 of 2 monitors on TB3 (DP-MST) dock no longer light up
hi - I almost certainly know the solution to this, the patches that we got from amd to do bandwidth checking in the DP MST helpers don't actually work correctly in a lot of cases and I need to fix them. I've just been busy on PTO and only just got back today, and have been busy with fixing a lot of RHEL stuff at the same time. I'll take a closer look at this soonb On Wed, 2020-02-26 at 16:15 +0100, Hans de Goede wrote: > Hi Lyude and everyone else, > > Lyude I'm mailing you about this because you have done a lot of > work on DP MST, but if this rings a bell to anyone else feel > free to weigh in on this. > > I'm currently using a Lenovo X1 7th gen + a Lenovo TB3 gen 2 dock > as my daily rider for testing purposes. When 5.6-rc1 came out I > noticed that only 1 of the 2 1920x1080@60 monitors on the dock > lights up. > > There are no kernel errors in the logs, but mutter/gnome-shell says: > > gnome-shell[1316]: Failed to post KMS update: Page flip of 93 failed > > With 93 being the crtc-id of the crtc used for the monitor which is > displaying black. Since then I've waited for 5.6-rc3 hoping that a > fix was already queued up, but 5.6-rc3 still has this problem. > > gnome-shell does behave as if all monitors are connected, so the > monitor is seen, but we are failing to actually send any frames > to it. > > I've put a log collected with drm.debug=0x104 here: > https://fedorapeople.org/~jwrdegoede/drm-debug.log > > This message stands out as pointing to the likely cause of this problem: > > [ 3.309061] [drm:intel_dump_pipe_config [i915]] MST master transcoder: > > > Regards, > > Hans > -- Cheers, Lyude Paul (she/her) Associate Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Re: [Intel-gfx] 5.6 DP-MST regression: 1 of 2 monitors on TB3 (DP-MST) dock no longer light up
On Thu, 2020-02-27 at 10:04 -0500, Mikita Lipski wrote: > > On 2/26/20 6:41 PM, Souza, Jose wrote: > > Hi Hans > > > > Just commenting in the "[3.309061] [drm:intel_dump_pipe_config > > [i915]] MST master transcoder: " message, it is the expected > > behaviour for anything older than Tigerlake, from TGL+ this will be set > > in MST mode. > > > > On Wed, 2020-02-26 at 18:52 +0100, Hans de Goede wrote: > > > Hi, > > > > > > On 2/26/20 5:05 PM, Alex Deucher wrote: > > > > On Wed, Feb 26, 2020 at 10:43 AM Hans de Goede > > > > wrote: > > > > > Hi, > > > > > > > > > > On 2/26/20 4:29 PM, Alex Deucher wrote: > > > > > > On Wed, Feb 26, 2020 at 10:16 AM Hans de Goede < > > > > > > hdego...@redhat.com> wrote: > > > > > > > Hi Lyude and everyone else, > > > > > > > > > > > > > > Lyude I'm mailing you about this because you have done a lot > > > > > > > of > > > > > > > work on DP MST, but if this rings a bell to anyone else feel > > > > > > > free to weigh in on this. > > > > > > > > > > > > Might be a duplicate of: > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitlab.freedesktop.org%2Fdrm%2Famd%2Fissues%2F1052&data=02%7C01%7Cmikita.lipski%40amd.com%7Ca48e7470afee41cb208508d7bb155ad0%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637183572706454329&sdata=AKmPhCqvvKtgzDBaobU4g74bErQQ7O3aL%2FJ8Al2Ey2I%3D&reserved=0 > > > > > > > > > > Looks like you are right, reverting the commit which the bisect > > > > > from that issue points to: > > > > > > > > > > cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST > > > > > atomic check") > > > > > > > > > > Fixes the issue for me. I will add a comment to the issue. > > > > > > > > > > Note I'm using integrated Intel gfx, so that means that this > > > > > issue > > > > > definitely is not amdgpu specific. > > > > > > > > > > > > > I'm not too familiar with the mst code, but I wonder if we were > > > > exceeding the bandwidth limits in some setups and it just happened > > > > to > > > > work, but now that we enforcing them, they don't which is correct, > > > > but > > > > a regression from some users' perspective? > > > > > > I seriously doubt that is the case according to: > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fsupport.lenovo.com%2Fnl%2Fen%2Fsolutions%2Fpd029622&data=02%7C01%7Cmikita.lipski%40amd.com%7Ca48e7470afee41cb208508d7bb155ad0%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637183572706454329&sdata=64uP50QojK2HkemDq3EGNKCVEgVl1ZxucyI%2F%2Bkk2Ng0%3D&reserved=0 > > > > > > The gen 2 tb3 dock can handle 2 external > > > displays at 3840*2160@60Hz together with the internal > > > panel being on and both my external displays run at > > > 1920x1080@60 so I'm consuming less then half of the > > > maximum bandwidth. > > > > > > There definitely is a bug somewhere in the > > > cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST > > > atomic check") > > > commit (or somewhere else and triggered by that commit). > > > > > > Regards, > > > > > > Hans > > + Lin Wyane, Lyude Paul > > Hi, > > Sorry I'm late responding to the thread. > The reason why this issue could have missed is because this patch was > pushed as a part of DSC MST patch series and with DSC the pbn is much > lower so it wouldn't fail this check. > > Anyways this check might have exposed a different bug in DRM. It seems > like the variable of available_pbn doesn't get updated on the ports in > the topology so the calculation of branch's bandwidth limit is not > correct, which would cause a branch bandwidth to be bottle-necked by > pbn_limit variable. > From DP 1.4 standart it seems like DRM should update available_pbn on > each port, when processing RESOURCE_STATUS_NOTIFY sideband message when > topology changes. Right now DRM doesn't seem to be doing anything about > it. Was it the intention, or has it just never implemented? If it the > intention, then the patch should be reverted till a new solution
Re: [Intel-gfx] [PATCH v4 13/14] drm/mst: Add support for QUERY_STREAM_ENCRYPTION_STATUS MST sideband message
Hey - I've got a good bit of stuff on my plate right now since I just got back from PTO and am going through my backlog of stuff, but I'll try to get this reviewed first chance I get On Tue, 2020-02-18 at 17:02 -0500, Sean Paul wrote: > From: Sean Paul > > Used to query whether an MST stream is encrypted or not. > > Cc: Lyude Paul > Signed-off-by: Sean Paul > > Changes in v4: > -Added to the set > --- > drivers/gpu/drm/drm_dp_mst_topology.c | 117 ++ > include/drm/drm_dp_helper.h | 3 + > include/drm/drm_dp_mst_helper.h | 44 ++ > 3 files changed, 164 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c > b/drivers/gpu/drm/drm_dp_mst_topology.c > index a811247cecfef..30b6dc6ce54c2 100644 > --- a/drivers/gpu/drm/drm_dp_mst_topology.c > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c > @@ -25,6 +25,7 @@ > #include > #include > #include > +#include > #include > #include > > @@ -418,6 +419,22 @@ drm_dp_encode_sideband_req(const struct > drm_dp_sideband_msg_req_body *req, > memcpy(&buf[idx], req->u.i2c_write.bytes, req- > >u.i2c_write.num_bytes); > idx += req->u.i2c_write.num_bytes; > break; > + case DP_QUERY_STREAM_ENC_STATUS: { > + const struct drm_dp_query_stream_enc_status *msg; > + > + msg = &req->u.enc_status; > + buf[idx] = msg->stream_id; > + idx++; > + memcpy(&buf[idx], msg->client_id, sizeof(msg->client_id)); > + idx += sizeof(msg->client_id); > + buf[idx] = 0; > + buf[idx] |= msg->stream_event & GENMASK(1, 0); > + buf[idx] |= msg->valid_stream_event ? BIT(2) : 0; > + buf[idx] |= (msg->stream_behavior & GENMASK(1, 0)) << 3; > + buf[idx] |= msg->valid_stream_behavior ? BIT(5) : 0; > + idx++; > + } > + break; > } > raw->cur_len = idx; > } > @@ -926,6 +943,34 @@ static bool > drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_ms > return true; > } > > +static bool > +drm_dp_sideband_parse_query_stream_enc_status( > + struct drm_dp_sideband_msg_rx *raw, > + struct drm_dp_sideband_msg_reply_body *repmsg) > +{ > + struct drm_dp_query_stream_enc_status_ack_reply *reply; > + > + reply = &repmsg->u.enc_status; > + > + reply->stream_id = raw->msg[3]; > + > + reply->reply_signed = raw->msg[2] & BIT(0); > + > + reply->hdcp_1x_device_present = raw->msg[2] & BIT(3); > + reply->hdcp_2x_device_present = raw->msg[2] & BIT(4); > + > + reply->query_capable_device_present = raw->msg[2] & BIT(5); > + reply->legacy_device_present = raw->msg[2] & BIT(6); > + reply->unauthorizable_device_present = raw->msg[2] & BIT(7); > + > + reply->auth_completed = !!(raw->msg[1] & BIT(3)); > + reply->encryption_enabled = !!(raw->msg[1] & BIT(4)); > + reply->repeater_present = !!(raw->msg[1] & BIT(5)); > + reply->state = (raw->msg[1] & GENMASK(7, 6)) >> 6; > + > + return true; > +} > + > static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, > struct drm_dp_sideband_msg_reply_body > *msg) > { > @@ -960,6 +1005,8 @@ static bool drm_dp_sideband_parse_reply(struct > drm_dp_sideband_msg_rx *raw, > return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg); > case DP_CLEAR_PAYLOAD_ID_TABLE: > return true; /* since there's nothing to parse */ > + case DP_QUERY_STREAM_ENC_STATUS: > + return drm_dp_sideband_parse_query_stream_enc_status(raw, > msg); > default: > DRM_ERROR("Got unknown reply 0x%02x (%s)\n", msg->req_type, > drm_dp_mst_req_type_str(msg->req_type)); > @@ -1113,6 +1160,25 @@ static int build_power_updown_phy(struct > drm_dp_sideband_msg_tx *msg, > return 0; > } > > +static int > +build_query_stream_enc_status(struct drm_dp_sideband_msg_tx *msg, u8 > stream_id, > + u8 *q_id) > +{ > + struct drm_dp_sideband_msg_req_body req; > + > + req.req_type = DP_QUERY_STREAM_ENC_STATUS; > + req.u.enc_status.stream_id = stream_id; > + memcpy(req.u.enc_status.client_id, q_id, > +sizeof(req.u.enc_status.client_id)); > + req.u.enc_st
Re: [Intel-gfx] [PATCH 6/9] drm/nouveau: Fix unused variable warning
On Mon, 2020-03-02 at 15:30 +0200, Laurent Pinchart wrote: > Hi Pankaj, > > Thank you for the patch. > > On Mon, Mar 02, 2020 at 06:26:46PM +0530, Pankaj Bharadiya wrote: > > nouveau_drm pointer is not getting used anymore in > > nv50_mstm_{register,destroy}_connector functions, hence remove it. > > > > This fixes below warning. > > > > drivers/gpu/drm/nouveau/dispnv50/disp.c: In function > > ‘nv50_mstm_destroy_connector’: > > drivers/gpu/drm/nouveau/dispnv50/disp.c:1263:22: warning: unused variable > > ‘drm’ [-Wunused-variable] > > struct nouveau_drm *drm = nouveau_drm(connector->dev); > > ^~~ > > drivers/gpu/drm/nouveau/dispnv50/disp.c: In function > > ‘nv50_mstm_register_connector’: > > drivers/gpu/drm/nouveau/dispnv50/disp.c:1274:22: warning: unused variable > > ‘drm’ [-Wunused-variable] > > struct nouveau_drm *drm = nouveau_drm(connector->dev); > > ^~~ > > As commented on 7/9, you should squash this with the patch that > introduces the warnings. Agreed, with the patches squashed you can count this as: Reviewed-by: Lyude Paul fwiw - completely optional but if you'd like, you can probably also go ahead and remove drm_dp_mst_topology_mgr_cbs.register_connector and drm_dp_mst_topology_mgr_cbs.destroy_connector and replace them with open- coding, since those callbacks are literally identical amongst every driver and don't do anything other then call drm_connector_register()/drm_connector_unregister()/drm_connector_put(). > > Signed-off-by: Pankaj Bharadiya > > --- > > drivers/gpu/drm/nouveau/dispnv50/disp.c | 3 --- > > 1 file changed, 3 deletions(-) > > > > diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c > > b/drivers/gpu/drm/nouveau/dispnv50/disp.c > > index 97dd50e2917d..4e164ad8003f 100644 > > --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c > > +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c > > @@ -1260,7 +1260,6 @@ static void > > nv50_mstm_destroy_connector(struct drm_dp_mst_topology_mgr *mgr, > > struct drm_connector *connector) > > { > > - struct nouveau_drm *drm = nouveau_drm(connector->dev); > > struct nv50_mstc *mstc = nv50_mstc(connector); > > > > drm_connector_unregister(&mstc->connector); > > @@ -1271,8 +1270,6 @@ nv50_mstm_destroy_connector(struct > > drm_dp_mst_topology_mgr *mgr, > > static void > > nv50_mstm_register_connector(struct drm_connector *connector) > > { > > - struct nouveau_drm *drm = nouveau_drm(connector->dev); > > - > > drm_connector_register(connector); > > } > > -- Cheers, Lyude Paul (she/her) Associate Software Engineer at Red Hat ___ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
[Intel-gfx] [PATCH v3] drm/i915: Force DPCD backlight mode on X1 Extreme 2nd Gen 4K AMOLED panel
The X1 Extreme is one of the systems that lies about which backlight interface that it uses in its VBIOS as PWM backlight controls don't work at all on this machine. It's possible that this panel could be one of the infamous ones that can switch between PWM mode and DPCD backlight control mode, but we haven't gotten any more details on this from Lenovo just yet. For the time being though, making sure the backlight 'just works' is a bit more important. So, add a quirk to force DPCD backlight controls on for these systems based on EDID (since this panel doesn't appear to fill in the device ID). Hopefully in the future we'll figure out a better way of probing this. Changes since v2: * The bugzilla URL is deprecated, bug reporting happens on gitlab now. Update the messages we print to reflect this * Also, take the opportunity to move FDO_BUG_URL out of i915_utils.c and into i915_utils.h so that other places which print things that aren't traditional errors but are worth filing bugs about, can actually use it. Signed-off-by: Lyude Paul Reviewed-by: Adam Jackson Cc: Jani Nikula --- drivers/gpu/drm/drm_dp_helper.c | 4 .../drm/i915/display/intel_dp_aux_backlight.c | 24 +++ drivers/gpu/drm/i915/i915_utils.c | 1 - drivers/gpu/drm/i915/i915_utils.h | 2 ++ include/drm/drm_dp_helper.h | 10 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 7be712c67aae..671f4d3968fc 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1295,6 +1295,10 @@ struct edid_quirk { * DP quirks in such cases. */ static const struct edid_quirk edid_quirk_list[] = { + /* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation +* only supports DPCD backlight controls +*/ + { MFG(0x4c, 0x83), PROD_ID(0x41, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, }; #undef MFG diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 48276237b362..3e706bb850a8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -328,15 +328,31 @@ intel_dp_aux_display_control_capable(struct intel_connector *connector) int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector) { struct intel_panel *panel = &intel_connector->panel; - struct drm_i915_private *dev_priv = to_i915(intel_connector->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(intel_connector->encoder); + struct drm_device *dev = intel_connector->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); if (i915_modparams.enable_dpcd_backlight == 0 || - (i915_modparams.enable_dpcd_backlight == -1 && - dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)) + !intel_dp_aux_display_control_capable(intel_connector)) return -ENODEV; - if (!intel_dp_aux_display_control_capable(intel_connector)) + /* +* There are a lot of machines that don't advertise the backlight +* control interface to use properly in their VBIOS, :\ +*/ + if (dev_priv->vbt.backlight.type != + INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE && + !drm_dp_has_quirk(&intel_dp->desc, intel_dp->edid_quirks, + DP_QUIRK_FORCE_DPCD_BACKLIGHT)) { + DRM_DEV_INFO(dev->dev, +"Panel advertises DPCD backlight support, but " +"VBT disagrees. If your backlight controls " +"don't work try booting with " +"i915.enable_dpcd_backlight=1. If your machine " +"needs this, please file a _new_ bug report on " +"drm/i915, see " FDO_BUG_URL " for details.\n"); return -ENODEV; + } panel->backlight.setup = intel_dp_aux_setup_backlight; panel->backlight.enable = intel_dp_aux_enable_backlight; diff --git a/drivers/gpu/drm/i915/i915_utils.c b/drivers/gpu/drm/i915/i915_utils.c index 632d6953c78d..029854ae65fc 100644 --- a/drivers/gpu/drm/i915/i915_utils.c +++ b/drivers/gpu/drm/i915/i915_utils.c @@ -8,7 +8,6 @@ #include "i915_drv.h" #include "i915_utils.h" -#define FDO_BUG_URL "https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs"; #define FDO_BUG_MSG "Please file a bug on drm/i915; see " FDO_BUG_URL " for details." void diff --git a/drivers/gpu/drm/i915/i915_utils.h b/d