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 | 165 ++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index e64abd3..6ef320b 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c @@ -120,6 +120,164 @@ int lspcon_ioa_write(struct i2c_adapter *adapter, u8 *buffer, return err; } +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 = lspcon_ioa_read(adapter, &data, LSPCON_I2C_ADDRESS, + LSPCON_MODE_CHECK_OFFSET, 1); + 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 = lspcon_ioa_write(&dig_port->dp.aux.ddc, &data, LSPCON_I2C_ADDRESS, + LSPCON_MODE_CHANGE_OFFSET, 1); + 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) +{ + int err = 0; + u8 sign[LSPCON_IDENTIFIER_LENGTH + 1] = {'\0', }; + struct intel_digital_port *dig_port = lspcon_to_dig_port(lspcon); + struct i2c_adapter *adapter = &dig_port->dp.aux.ddc; + + /* + * Identifier: First 15 bytes are ascii for "DP-HDMI ADAPTOR". The 16th + * byte defines if thats a LSPCON or any other dongle. If byte 16 =0xa8 + * its LSPCON + */ + + /* Read 16 bytes from I2C reg 0x80, offset 0x0 I2C-over-aux */ + err = lspcon_ioa_read(adapter, sign, LSPCON_I2C_ADDRESS, + LSPCON_ADAPTER_SIGN_OFFSET, LSPCON_IDENTIFIER_LENGTH); + if (err < 0) { + DRM_ERROR("Error reading lspcon sign (0x80, 0x0)\n"); + return false; + } + + /* Check sign */ + if (strncmp((void *)sign, "DP-HDMI ADAPTOR", + LSPCON_IDENTIFIER_LENGTH - 1)) { + DRM_ERROR("Cant detect adaptor sign, its %s\n", sign); + return false; + } + + /* Identify LSPCON */ + if (sign[LSPCON_IDENTIFIER_OFFSET] != LSPCON_ADAPTER) { + DRM_ERROR("Found non LSPCON adaptor\n"); + return false; + } + + /* yay ... found a LSPCON */ + DRM_DEBUG_DRIVER("LSPCON adaptor 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; +} + static int lspcon_get_edid_over_aux(void *data, u8 *buf, unsigned int block, size_t len) { @@ -333,6 +491,7 @@ void 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; @@ -411,6 +570,12 @@ void 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("LSPCON connector init done\n"); return; -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx