This patch adds lspcon's internal functions, which work on the probe layer, and indicate the working status of lspcon, which are mostly: probe: A lspcon device is probed only once, during boot time, as its always present with the device, next to port. So the i2c_over_aux channel is alwyas read/writeable if DC is powered on. If VBT says that this port contains lspcon, we check and probe the HW to verify and initialize it. get_mode: This function indicates the current mode of operation of lspcon (ls or pcon mode) change_mode: This function can change the lspcon's mode of operation to desired mode. Signed-off-by: Shashank Sharma <shashank.sharma@xxxxxxxxx> Signed-off-by: Akashdeep Sharma <Akashdeep.sharma@xxxxxxxxx> --- drivers/gpu/drm/i915/intel_lspcon.c | 145 ++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index c3c1cd2..617fe3f 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c @@ -61,6 +61,144 @@ static struct intel_lspcon return enc_to_lspcon(&intel_attached_encoder(connector)->base); } +enum lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) +{ + u8 data; + int err = 0; + struct intel_digital_port *dig_port = lspcon_to_dig_port(lspcon); + struct i2c_adapter *adapter = &dig_port->dp.aux.ddc; + + /* Read Status: i2c over aux */ + err = drm_dp_dual_mode_ioa_read(adapter, &data, + LSPCON_MODE_CHECK_OFFSET, sizeof(data)); + if (err < 0) { + DRM_ERROR("LSPCON read mode ioa (0x80, 0x41) failed\n"); + return lspcon_mode_invalid; + } + + DRM_DEBUG_DRIVER("LSPCON mode (0x80, 0x41) = %x\n", (unsigned int)data); + return data & LSPCON_MODE_MASK ? lspcon_mode_pcon : lspcon_mode_ls; +} + +int lspcon_change_mode(struct intel_lspcon *lspcon, + enum lspcon_mode mode, bool force) +{ + u8 data; + int err; + int time_out = 200; + enum lspcon_mode current_mode; + struct intel_digital_port *dig_port = lspcon_to_dig_port(lspcon); + + current_mode = lspcon_get_current_mode(lspcon); + if (current_mode == lspcon_mode_invalid) { + DRM_ERROR("Failed to get current LSPCON mode\n"); + return -EFAULT; + } + + if (current_mode == mode && !force) { + DRM_DEBUG_DRIVER("Current mode = desired LSPCON mode\n"); + return 0; + } + + if (mode == lspcon_mode_ls) + data = ~LSPCON_MODE_MASK; + else + data = LSPCON_MODE_MASK; + + /* Change mode */ + err = drm_dp_dual_mode_ioa_write(&dig_port->dp.aux.ddc, &data, + LSPCON_MODE_CHANGE_OFFSET, sizeof(data)); + if (err < 0) { + DRM_ERROR("LSPCON mode change failed\n"); + return err; + } + + /* + * Confirm mode change by reading the status bit. + * Sometimes, it takes a while to change the mode, + * so wait and retry until time out or done. + */ + while (time_out) { + current_mode = lspcon_get_current_mode(lspcon); + if (current_mode != mode) { + mdelay(10); + time_out -= 10; + } else { + lspcon->mode_of_op = mode; + DRM_DEBUG_DRIVER("LSPCON mode changed to %s\n", + mode == lspcon_mode_ls ? "LS" : "PCON"); + return 0; + } + } + + DRM_ERROR("LSPCON mode change timed out\n"); + return -EFAULT; +} + +bool lspcon_detect_identifier(struct intel_lspcon *lspcon) +{ + enum drm_dp_dual_mode_type adaptor_type; + struct intel_digital_port *dig_port = lspcon_to_dig_port(lspcon); + struct i2c_adapter *adapter = &dig_port->dp.aux.ddc; + + /* Lets probe the adaptor and check its type */ + adaptor_type = drm_dp_dual_mode_detect(adapter); + if (adaptor_type != DRM_DP_DUAL_MODE_TYPE2_LSPCON) { + DRM_DEBUG_DRIVER("No LSPCON detected, found %s\n", + drm_dp_get_dual_mode_type_name(adaptor_type)); + return false; + } + + /* Yay ... got a LSPCON device */ + DRM_DEBUG_DRIVER("LSPCON detected\n"); + return true; +} + +enum lspcon_mode lspcon_probe(struct intel_lspcon *lspcon) +{ + enum lspcon_mode current_mode; + + /* Detect a valid lspcon */ + if (!lspcon_detect_identifier(lspcon)) { + DRM_DEBUG_DRIVER("Failed to find LSPCON identifier\n"); + return false; + } + + /* LSPCON's mode of operation */ + current_mode = lspcon_get_current_mode(lspcon); + if (current_mode == lspcon_mode_invalid) { + DRM_ERROR("Failed to read LSPCON mode\n"); + return false; + } + + /* All is well */ + lspcon->mode_of_op = current_mode; + lspcon->active = true; + return current_mode; +} + +bool lspcon_device_init(struct intel_lspcon *lspcon) +{ + + /* Lets check LSPCON now, probe the HW status */ + lspcon->active = false; + lspcon->mode_of_op = lspcon_mode_invalid; + if (!lspcon_probe(lspcon)) { + DRM_ERROR("Failed to probe lspcon"); + return false; + } + + /* We wish to keep LSPCON in LS mode */ + if (lspcon->active && lspcon->mode_of_op != lspcon_mode_ls) { + if (lspcon_change_mode(lspcon, lspcon_mode_ls, true) < 0) { + DRM_ERROR("LSPCON mode change to LS failed\n"); + return false; + } + } + DRM_DEBUG_DRIVER("LSPCON init success\n"); + return true; +} + struct edid *lspcon_get_edid(struct intel_lspcon *lspcon, struct drm_connector *connector) { @@ -233,6 +371,7 @@ int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port) struct intel_encoder *intel_encoder = &intel_dig_port->base; struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_lspcon *lspcon = &intel_dig_port->lspcon; struct intel_connector *intel_connector; struct drm_connector *connector; enum port port = intel_dig_port->port; @@ -314,6 +453,12 @@ int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port) I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); } + /* Now initialize the LSPCON device */ + if (!lspcon_device_init(lspcon)) { + DRM_ERROR("LSPCON device init failed\n"); + goto fail; + } + DRM_DEBUG_DRIVER("Success: LSPCON connector init\n"); return 0; -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx