On eDP each low level transfer is wrapped by pps_lock()/unlock() and edp_panel_vdd_on()/off(). Since PPS locking can be quite expensive and each call to master_xfer() or dpcd_access() may call these low level transfer functions many times it may introduce a considerable delay. Move locking/VDD enablement to the top so that it is performed only once per master_xfer() or dpcd_access(). This fixes https://bugs.freedesktop.org/show_bug.cgi?id=86201 Signed-off-by: Egbert Eich <eich@xxxxxxx> --- drivers/gpu/drm/i915/intel_dp.c | 116 ++++++++++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_drv.h | 5 ++ 2 files changed, 100 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a24c8cc7..164f80e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -807,19 +807,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint32_t status; int try, clock = 0; bool has_aux_irq = HAS_AUX_IRQ(dev); - bool vdd = false; - - if (is_edp(intel_dp)) { - pps_lock(intel_dp); - - /* - * We will be called with VDD already enabled for dpcd/edid/oui reads. - * In such cases we want to leave VDD enabled and it's up to upper layers - * to turn it off. But for eg. i2c-dev access we need to turn it on/off - * ourselves. - */ - vdd = edp_panel_vdd_on(intel_dp); - } /* dp aux is extremely sensitive to irq latency, hence request the * lowest possible wakeup latency and so prevent the cpu from going into @@ -926,13 +913,6 @@ out: pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); intel_aux_display_runtime_put(dev_priv); - if (is_edp(intel_dp)) { - if (vdd) - edp_panel_vdd_off(intel_dp, false); - - pps_unlock(intel_dp); - } - return ret; } @@ -1001,6 +981,97 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return ret; } +int intel_edp_dpcd_access(struct drm_dp_aux *aux, u8 request, + unsigned int offset, void *buffer, size_t size) +{ + struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); + bool vdd = false; + int ret; + + pps_lock(intel_dp); + /* + * We will be called with VDD already enabled for dpcd/edid/oui reads. + * In such cases we want to leave VDD enabled and it's up to upper + * layers to turn it off. But for eg. i2c-dev access we need to turn + * it on/off ourselves. + */ + vdd = edp_panel_vdd_on(intel_dp); + + ret = drm_dp_dpcd_access(aux, request, offset, buffer, size); + + if (vdd) + edp_panel_vdd_off(intel_dp, false); + + pps_unlock(intel_dp); + + return ret; +} + +static int +intel_edp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct drm_dp_aux *aux = adapter->algo_data; + struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); + bool vdd = false; + int ret; + + pps_lock(intel_dp); + /* + * We will be called with VDD already enabled for dpcd/edid/oui reads. + * In such cases we want to leave VDD enabled and it's up to upper + * layers to turn it off. But for eg. i2c-dev access we need to turn + * it on/off ourselves. + */ + vdd = edp_panel_vdd_on(intel_dp); + + ret = drm_dp_i2c_xfer(adapter, msgs, num); + + if (vdd) + edp_panel_vdd_off(intel_dp, false); + + pps_unlock(intel_dp); + + return ret; +} + +static u32 intel_edp_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm intel_edp_i2c_algo = { + .functionality = intel_edp_i2c_functionality, + .master_xfer = intel_edp_i2c_xfer, +}; + +/* + * intel_edp_aux_register() - Intel replacement for + * drm_dp_aux_register(). + */ +static int intel_edp_aux_register(struct drm_dp_aux *aux) +{ + mutex_init(&aux->hw_mutex); + aux->dpcd_access = intel_edp_dpcd_access; + + aux->ddc.algo = &intel_edp_i2c_algo; + aux->ddc.algo_data = aux; + aux->ddc.retries = 3; + + aux->ddc.class = I2C_CLASS_DDC; + aux->ddc.owner = THIS_MODULE; + aux->ddc.dev.parent = aux->dev; + aux->ddc.dev.of_node = aux->dev->of_node; + + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); + + return i2c_add_adapter(&aux->ddc); +} + static void intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) { @@ -1050,7 +1121,10 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) DRM_DEBUG_KMS("registering %s bus for %s\n", name, connector->base.kdev->kobj.name); - ret = drm_dp_aux_register(&intel_dp->aux); + if (!is_edp(intel_dp)) + ret = drm_dp_aux_register(&intel_dp->aux); + else + ret = intel_edp_aux_register(&intel_dp->aux); if (ret < 0) { DRM_ERROR("drm_dp_aux_register() for %s failed (%d)\n", name, ret); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 44b153c5..783261a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -589,6 +589,11 @@ struct intel_dp { uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_aux aux; + struct i2c_algorithm ddc_algo; + int (*aux_master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); + int (*dpcd_access)(struct drm_dp_aux *aux, u8 request, + unsigned int offset, void *buffer, size_t size); uint8_t train_set[4]; int panel_power_up_delay; int panel_power_down_delay; -- 1.8.4.5 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx