static bool lspcon_detect_vendor(struct intel_lspcon *lspcon) { struct intel_dp *dp = lspcon_to_intel_dp(lspcon); struct drm_dp_dpcd_ident *ident; u32 vendor_oui; if (drm_dp_read_desc(&dp->aux, &dp->desc, drm_dp_is_branch(dp->dpcd))) { DRM_ERROR("Can't read description\n"); return false; } ident = &dp->desc.ident; vendor_oui = (ident->oui[0] << 16) | (ident->oui[1] << 8) | ident->oui[2]; switch (vendor_oui) { case LSPCON_VENDOR_MCA_OUI: lspcon->vendor = LSPCON_VENDOR_MCA; DRM_DEBUG_KMS("Vendor: Mega Chips\n"); break; case LSPCON_VENDOR_PARADE_OUI: lspcon->vendor = LSPCON_VENDOR_PARADE; DRM_DEBUG_KMS("Vendor: Parade Tech\n"); break; default: DRM_ERROR("Invalid/Unknown vendor OUI\n"); return false; } return true; }
static int lspcon_change_mode(struct intel_lspcon *lspcon, enum drm_lspcon_mode mode) { int err; enum drm_lspcon_mode current_mode; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; err = drm_lspcon_get_mode(adapter, ¤t_mode); if (err) { DRM_ERROR("Error reading LSPCON mode\n"); return err; } if (current_mode == mode) { DRM_DEBUG_KMS("Current mode = desired LSPCON mode\n"); return 0; } err = drm_lspcon_set_mode(adapter, mode); if (err < 0) { DRM_ERROR("LSPCON mode change failed\n"); return err; } lspcon->mode = mode; DRM_DEBUG_KMS("LSPCON mode changed done\n"); return 0; }
static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) { struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon); unsigned long start = jiffies; if (!lspcon->desc_valid) return; while (1) { struct intel_dp_desc desc; /* * The w/a only applies in PCON mode and we don't expect any * AUX errors. */ if (!__intel_dp_read_desc(intel_dp, &desc)) return; if (!memcmp(&intel_dp->desc, &desc, sizeof(desc))) { DRM_DEBUG_KMS("LSPCON recovering in PCON mode after %u ms\n", jiffies_to_msecs(jiffies - start)); return; } if (time_after(jiffies, start + msecs_to_jiffies(1000))) break; usleep_range(10000, 15000); } DRM_DEBUG_KMS("LSPCON DP descriptor mismatch after resume\n"); }
static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) { enum drm_lspcon_mode current_mode; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; if (drm_lspcon_get_mode(adapter, ¤t_mode)) { DRM_ERROR("Error reading LSPCON mode\n"); return DRM_LSPCON_MODE_INVALID; } return current_mode; }
static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) { enum drm_lspcon_mode current_mode = DRM_LSPCON_MODE_INVALID; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; if (drm_lspcon_get_mode(adapter, ¤t_mode)) DRM_ERROR("Error reading LSPCON mode\n"); else DRM_DEBUG_KMS("Current LSPCON mode %s\n", current_mode == DRM_LSPCON_MODE_PCON ? "PCON" : "LS"); return current_mode; }
static bool lspcon_wake_native_aux_ch(struct intel_lspcon *lspcon) { uint8_t rev; if (drm_dp_dpcd_readb(&lspcon_to_intel_dp(lspcon)->aux, DP_DPCD_REV, &rev) != 1) { DRM_DEBUG_KMS("Native AUX CH down\n"); return false; } DRM_DEBUG_KMS("Native AUX CH up, DPCD version: %d.%d\n", rev >> 4, rev & 0xf); return true; }
static bool lspcon_probe(struct intel_lspcon *lspcon) { int retry; enum drm_dp_dual_mode_type adaptor_type; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; enum drm_lspcon_mode expected_mode; expected_mode = lspcon_wake_native_aux_ch(lspcon) ? DRM_LSPCON_MODE_PCON : DRM_LSPCON_MODE_LS; /* Lets probe the adaptor and check its type */ for (retry = 0; retry < 6; retry++) { if (retry) usleep_range(500, 1000); adaptor_type = drm_dp_dual_mode_detect(adapter); if (adaptor_type == DRM_DP_DUAL_MODE_LSPCON) break; } if (adaptor_type != DRM_DP_DUAL_MODE_LSPCON) { DRM_DEBUG_KMS("No LSPCON detected, found %s\n", drm_dp_get_dual_mode_type_name(adaptor_type)); return false; } /* Yay ... got a LSPCON device */ DRM_DEBUG_KMS("LSPCON detected\n"); lspcon->mode = lspcon_wait_mode(lspcon, expected_mode); /* * In the SW state machine, lets Put LSPCON in PCON mode only. * In this way, it will work with both HDMI 1.4 sinks as well as HDMI * 2.0 sinks. */ if (lspcon->mode != DRM_LSPCON_MODE_PCON) { if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON) < 0) { DRM_ERROR("LSPCON mode change to PCON failed\n"); return false; } } return true; }
static bool lspcon_probe(struct intel_lspcon *lspcon) { enum drm_dp_dual_mode_type adaptor_type; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->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_LSPCON) { DRM_DEBUG_KMS("No LSPCON detected, found %s\n", drm_dp_get_dual_mode_type_name(adaptor_type)); return false; } /* Yay ... got a LSPCON device */ DRM_DEBUG_KMS("LSPCON detected\n"); lspcon->mode = lspcon_get_current_mode(lspcon); lspcon->active = true; return true; }
static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) { struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); unsigned long start = jiffies; while (1) { if (intel_digital_port_connected(&dig_port->base)) { DRM_DEBUG_KMS("LSPCON recovering in PCON mode after %u ms\n", jiffies_to_msecs(jiffies - start)); return; } if (time_after(jiffies, start + msecs_to_jiffies(1000))) break; usleep_range(10000, 15000); } DRM_DEBUG_KMS("LSPCON DP descriptor mismatch after resume\n"); }