[Intel-gfx] [PATCH 6/7] drm/i915: Add lspcon core functions
Shashank Sharma
shashank.sharma at intel.com
Tue Mar 22 14:25:07 UTC 2016
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 at intel.com>
Signed-off-by: Akashdeep Sharma <Akashdeep.sharma at intel.com>
---
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
More information about the Intel-gfx
mailing list