lspcon is a tricky device which acts as DP in some cases and as HDMI in some. Here is how: - lspcon needs DP for all the i2c_ove_aux read/write transitions so it needs to have some DP level initializations - lspcon is detected by userspace/sink as HDMI device, so it needs to be detectd as HDMI device. This patch adds a custom connector for lspcon device, which can pick and chose what it wants from existing functionality. This patch also adds functions to init dp and hdmi to the minimum need, and then play around with them. Signed-off-by: Shashank Sharma <shashank.sharma@xxxxxxxxx> --- drivers/gpu/drm/i915/intel_ddi.c | 6 ++++ drivers/gpu/drm/i915/intel_dp.c | 31 ++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 8 +++++ drivers/gpu/drm/i915/intel_hdmi.c | 17 +++++++++ drivers/gpu/drm/i915/intel_lspcon.c | 72 +++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 91654ff..f68c257 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -2169,6 +2169,12 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); intel_encoder->cloneable = 0; + /* Check if LSPCON is configured on this port */ + if (intel_bios_is_lspcon_preset(dev, intel_dig_port->port)) { + intel_lspcon_init_connector(intel_dig_port); + return; + } + if (init_dp) { if (!intel_ddi_init_dp_connector(intel_dig_port)) goto err; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3ff8f1d..6005c26 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -5895,6 +5895,37 @@ fail: return false; } +int intel_dp_init_minimum(struct intel_digital_port *intel_dig_port, + struct intel_connector *intel_connector) +{ + int ret; + enum port port = intel_dig_port->port; + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); + if (WARN(intel_dig_port->max_lanes < 1, + "Not enough lanes (%d) for DP on port %c\n", + intel_dig_port->max_lanes, port_name(port))) + return -EINVAL; + + intel_dp->pps_pipe = INVALID_PIPE; + intel_dp->get_aux_clock_divider = skl_get_aux_clock_divider; + intel_dp->get_aux_send_ctl = skl_get_aux_send_ctl; + intel_dp->prepare_link_retrain = intel_ddi_prepare_link_retrain; + intel_dp->DP = I915_READ(intel_dp->output_reg); + intel_dp->attached_connector = intel_connector; + INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, + edp_panel_vdd_work); + + ret = intel_dp_aux_init(intel_dp, intel_connector); + if (ret) + DRM_ERROR("Aux init for LSPCON failed\n"); + + return ret; +} + void intel_dp_init(struct drm_device *dev, i915_reg_t output_reg, enum port port) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index f3e7e52..09273d5 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1327,6 +1327,9 @@ void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp); bool intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]); +int intel_dp_init_minimum(struct intel_digital_port *intel_dig_port, + struct intel_connector *intel_connector); + /* intel_dp_mst.c */ int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); @@ -1339,6 +1342,9 @@ void intel_dsi_init(struct drm_device *dev); void intel_dvo_init(struct drm_device *dev); +/* intel_lspcon.c */ +void intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port); + /* legacy fbdev emulation in intel_fbdev.c */ #ifdef CONFIG_DRM_FBDEV_EMULATION extern int intel_fbdev_init(struct drm_device *dev); @@ -1392,6 +1398,8 @@ void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv); void intel_hdmi_init(struct drm_device *dev, i915_reg_t hdmi_reg, enum port port); void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); +int intel_hdmi_init_minimum(struct intel_digital_port *intel_dig_port, + struct intel_connector *intel_connector); struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e2dab48..9fcbbdf 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2035,6 +2035,23 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; } +int intel_hdmi_init_minimum(struct intel_digital_port *intel_dig_port, + struct intel_connector *intel_connector) +{ + struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; + + if (WARN(intel_dig_port->max_lanes < 4, + "Not enough lanes (%d) for HDMI on port %c\n", + intel_dig_port->max_lanes, port_name(intel_dig_port->port))) + return -EINVAL; + + intel_hdmi->write_infoframe = hsw_write_infoframe; + intel_hdmi->set_infoframes = hsw_set_infoframes; + intel_hdmi->infoframe_enabled = hsw_infoframe_enabled; + intel_hdmi->attached_connector = intel_connector; + return 0; +} + void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector) { diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index f5c91db..9d5ed0c 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c @@ -112,3 +112,75 @@ int lspcon_ioa_write(struct i2c_adapter *adapter, u8 *buffer, return err; } + +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_connector *intel_connector; + struct drm_connector *connector; + enum port port = intel_dig_port->port; + + intel_connector = intel_connector_alloc(); + if (!intel_connector) + return; + + connector = &intel_connector->base; + connector->interlace_allowed = true; + connector->doublescan_allowed = 0; + + /* init DP */ + if (intel_dp_init_minimum(intel_dig_port, intel_connector)) { + DRM_ERROR("DP init for LSPCON failed\n"); + return; + } + + /* init HDMI */ + if (intel_hdmi_init_minimum(intel_dig_port, intel_connector)) { + DRM_ERROR("HDMI init for LSPCON failed\n"); + return; + } + + /* Set up the hotplug pin. */ + switch (port) { + case PORT_A: + intel_encoder->hpd_pin = HPD_PORT_A; + break; + case PORT_B: + intel_encoder->hpd_pin = HPD_PORT_B; + if (IS_BXT_REVID(dev, 0, BXT_REVID_A1)) + intel_encoder->hpd_pin = HPD_PORT_A; + break; + case PORT_C: + intel_encoder->hpd_pin = HPD_PORT_C; + break; + case PORT_D: + intel_encoder->hpd_pin = HPD_PORT_D; + break; + case PORT_E: + intel_encoder->hpd_pin = HPD_PORT_E; + break; + default: + BUG(); + } + + /* + * On BXT A0/A1, sw needs to activate DDIA HPD logic and + * interrupts to check the external panel connection. + */ + if (IS_BXT_REVID(dev, 0, BXT_REVID_A1) && port == PORT_B) + dev_priv->hotplug.irq_port[PORT_A] = intel_dig_port; + else + dev_priv->hotplug.irq_port[port] = intel_dig_port; + + /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written + * 0xd. Failure to do so will result in spurious interrupts being + * generated on the port when a cable is not attached. + */ + if (IS_G4X(dev) && !IS_GM45(dev)) { + u32 temp = I915_READ(PEG_BAND_GAP_DATA); + + I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); + } +} -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx