diff mbox

[09/12] drm/i915: Add and register lspcon connector functions

Message ID 1459771308-4405-9-git-send-email-shashank.sharma@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sharma, Shashank April 4, 2016, 12:01 p.m. UTC
This patch adds various lspcon connector functions. Some
of the functions are newly written, to meet the specific
needs of lspcon HW, whereas few of them are just an
abstraction layer on existing HDMI connector functions.

Signed-off-by: Shashank Sharma <shashank.sharma@intel.com>
---
 drivers/gpu/drm/i915/intel_drv.h     |  11 +-
 drivers/gpu/drm/i915/intel_hdmi.c    |   8 +-
 drivers/gpu/drm/i915/intel_hotplug.c |   2 +-
 drivers/gpu/drm/i915/intel_lspcon.c  | 195 ++++++++++++++++++++++++++++++++++-
 4 files changed, 207 insertions(+), 9 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d38db7d..a6ec946 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1404,7 +1404,12 @@  int intel_hdmi_init_minimum(struct intel_digital_port *intel_dig_port,
 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);
-
+int intel_hdmi_get_modes(struct drm_connector *connector);
+int intel_hdmi_set_property(struct drm_connector *connector,
+		struct drm_property *property, uint64_t val);
+void intel_hdmi_destroy(struct drm_connector *connector);
+void intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi,
+		struct drm_connector *connector);
 
 /* intel_lvds.c */
 void intel_lvds_init(struct drm_device *dev);
@@ -1493,6 +1498,10 @@  bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv,
 void intel_display_power_put(struct drm_i915_private *dev_priv,
 			     enum intel_display_power_domain domain);
 
+/* intel_hotplug.c */
+bool intel_hpd_irq_event(struct drm_device *dev,
+		struct drm_connector *connector);
+
 static inline void
 assert_rpm_device_not_suspended(struct drm_i915_private *dev_priv)
 {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 92f5094..3f7919d 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1540,7 +1540,7 @@  intel_hdmi_force(struct drm_connector *connector)
 	hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
 }
 
-static int intel_hdmi_get_modes(struct drm_connector *connector)
+int intel_hdmi_get_modes(struct drm_connector *connector)
 {
 	struct edid *edid;
 
@@ -1564,7 +1564,7 @@  intel_hdmi_detect_audio(struct drm_connector *connector)
 	return has_audio;
 }
 
-static int
+int
 intel_hdmi_set_property(struct drm_connector *connector,
 			struct drm_property *property,
 			uint64_t val)
@@ -2089,7 +2089,7 @@  static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
 	}
 }
 
-static void intel_hdmi_destroy(struct drm_connector *connector)
+void intel_hdmi_destroy(struct drm_connector *connector)
 {
 	kfree(to_intel_connector(connector)->detect_edid);
 	drm_connector_cleanup(connector);
@@ -2118,7 +2118,7 @@  static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
 	.destroy = intel_encoder_destroy,
 };
 
-static void
+void
 intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
 {
 	intel_attach_force_audio_property(connector);
diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c
index bee6730..11a3e02 100644
--- a/drivers/gpu/drm/i915/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/intel_hotplug.c
@@ -226,7 +226,7 @@  static void intel_hpd_irq_storm_reenable_work(struct work_struct *work)
 	intel_runtime_pm_put(dev_priv);
 }
 
-static bool intel_hpd_irq_event(struct drm_device *dev,
+bool intel_hpd_irq_event(struct drm_device *dev,
 				struct drm_connector *connector)
 {
 	enum drm_connector_status old_status;
diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c
index e179758..c3c1cd2 100644
--- a/drivers/gpu/drm/i915/intel_lspcon.c
+++ b/drivers/gpu/drm/i915/intel_lspcon.c
@@ -55,6 +55,178 @@  struct intel_lspcon *enc_to_lspcon(struct drm_encoder *encoder)
 	return &intel_dig_port->lspcon;
 }
 
+static struct intel_lspcon
+*intel_attached_lspcon(struct drm_connector *connector)
+{
+	return enc_to_lspcon(&intel_attached_encoder(connector)->base);
+}
+
+struct edid *lspcon_get_edid(struct intel_lspcon *lspcon, struct drm_connector
+						*connector)
+{
+	struct edid *edid = NULL;
+	struct intel_digital_port *dig_port = lspcon_to_dig_port(lspcon);
+	struct i2c_adapter *adapter = &dig_port->dp.aux.ddc;
+
+	if (lspcon->mode_of_op != lspcon_mode_ls) {
+		DRM_ERROR("EDID read supported in LS mode only\n");
+		return false;
+	}
+
+	/* LS mode, getting EDID using I2C over Aux */
+	edid = drm_do_get_edid(connector, drm_dp_dual_mode_get_edid,
+			(void *)adapter);
+	return edid;
+}
+
+static void
+lspcon_unset_edid(struct drm_connector *connector)
+{
+	struct intel_lspcon *lspcon = intel_attached_lspcon(connector);
+	struct intel_hdmi *intel_hdmi = lspcon_to_hdmi(lspcon);
+
+	intel_hdmi->has_hdmi_sink = false;
+	intel_hdmi->has_audio = false;
+	intel_hdmi->rgb_quant_range_selectable = false;
+
+	kfree(to_intel_connector(connector)->detect_edid);
+	to_intel_connector(connector)->detect_edid = NULL;
+}
+
+static bool
+lspcon_set_edid(struct drm_connector *connector, bool force)
+{
+	struct drm_i915_private *dev_priv = to_i915(connector->dev);
+	struct intel_lspcon *lspcon = intel_attached_lspcon(connector);
+	struct intel_hdmi *intel_hdmi = lspcon_to_hdmi(lspcon);
+	struct edid *edid = NULL;
+	bool connected = false;
+
+	if (force) {
+		intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+		edid = lspcon_get_edid(lspcon, connector);
+		intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+	}
+
+	to_intel_connector(connector)->detect_edid = edid;
+	if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
+		intel_hdmi->rgb_quant_range_selectable =
+			drm_rgb_quant_range_selectable(edid);
+
+		intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
+		if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
+			intel_hdmi->has_audio =
+				intel_hdmi->force_audio == HDMI_AUDIO_ON;
+
+		if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
+			intel_hdmi->has_hdmi_sink =
+				drm_detect_hdmi_monitor(edid);
+
+		connected = true;
+	}
+	return connected;
+}
+
+static enum drm_connector_status
+lspcon_detect(struct drm_connector *connector, bool force)
+{
+	enum drm_connector_status status;
+	struct drm_i915_private *dev_priv = to_i915(connector->dev);
+	struct intel_lspcon *lspcon = intel_attached_lspcon(connector);
+	struct intel_hdmi *intel_hdmi = lspcon_to_hdmi(lspcon);
+
+	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+		      connector->base.id, connector->name);
+	intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+
+	lspcon_unset_edid(connector);
+	if (lspcon_set_edid(connector, true)) {
+		DRM_DEBUG_DRIVER("HDMI connected\n");
+		hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
+		status = connector_status_connected;
+	} else {
+		DRM_DEBUG_DRIVER("HDMI disconnected\n");
+		status = connector_status_disconnected;
+	}
+	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+	return status;
+}
+
+static int
+lspcon_set_property(struct drm_connector *connector,
+			struct drm_property *property,
+			uint64_t val)
+{
+	return intel_hdmi_set_property(connector, property, val);
+}
+
+static int
+lspcon_get_modes(struct drm_connector *connector)
+{
+	return intel_hdmi_get_modes(connector);
+}
+
+static void
+lspcon_destroy(struct drm_connector *connector)
+{
+	intel_hdmi_destroy(connector);
+}
+
+static enum drm_mode_status
+lspcon_mode_valid(struct drm_connector *connector,
+		      struct drm_display_mode *mode)
+{
+	int clock = mode->clock;
+	int max_dotclk = 675000; /* 4k@60 */
+	struct drm_device *dev = connector->dev;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+		return MODE_NO_DBLESCAN;
+
+	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+		DRM_MODE_FLAG_3D_FRAME_PACKING)
+		clock *= 2;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		clock *= 2;
+
+	if (clock < 25000)
+		return MODE_CLOCK_LOW;
+
+	if (clock > max_dotclk)
+		return MODE_CLOCK_HIGH;
+
+	/* BXT DPLL can't generate 223-240 MHz */
+	if (IS_BROXTON(dev) && clock > 223333 && clock < 240000)
+		return MODE_CLOCK_RANGE;
+
+	/* todo: check for 12bpc here */
+	return MODE_OK;
+}
+
+void lspcon_add_properties(struct intel_digital_port *dig_port,
+		struct drm_connector *connector)
+{
+	intel_hdmi_add_properties(&dig_port->hdmi, connector);
+}
+
+static const struct drm_connector_funcs lspcon_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = lspcon_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.set_property = lspcon_set_property,
+	.atomic_get_property = intel_connector_atomic_get_property,
+	.destroy = lspcon_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+};
+
+static const struct drm_connector_helper_funcs lspcon_connector_helper_funcs = {
+	.get_modes = lspcon_get_modes,
+	.mode_valid = lspcon_mode_valid,
+	.best_encoder = intel_best_encoder,
+};
+
 int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port)
 {
 	int ret;
@@ -73,18 +245,30 @@  int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port)
 	connector->interlace_allowed = true;
 	connector->doublescan_allowed = 0;
 
+	/* Load connector */
+	drm_connector_init(dev, connector, &lspcon_connector_funcs,
+			DRM_MODE_CONNECTOR_DisplayPort);
+	drm_connector_helper_add(connector, &lspcon_connector_helper_funcs);
+	intel_connector_attach_encoder(intel_connector, intel_encoder);
+	drm_connector_register(connector);
+
+	/* Add properties and functions */
+	lspcon_add_properties(intel_dig_port, connector);
+	intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
+	i915_debugfs_connector_add(connector);
+
 	/* init DP */
 	ret = intel_dp_init_minimum(intel_dig_port, intel_connector);
 	if (ret) {
 		DRM_ERROR("DP init for LSPCON failed\n");
-		return ret;
+		goto fail;
 	}
 
 	/* init HDMI */
 	ret = intel_hdmi_init_minimum(intel_dig_port, intel_connector);
 	if (ret) {
 		DRM_ERROR("HDMI init for LSPCON failed\n");
-		return ret;
+		goto fail;
 	}
 
 	/* Set up the hotplug pin. */
@@ -108,7 +292,7 @@  int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port)
 		break;
 	default:
 		DRM_ERROR("Invalid port to configure LSPCON\n");
-		return -EINVAL;
+		ret = -EINVAL;
 	}
 
 	/*
@@ -132,4 +316,9 @@  int intel_lspcon_init_connector(struct intel_digital_port *intel_dig_port)
 
 	DRM_DEBUG_DRIVER("Success: LSPCON connector init\n");
 	return 0;
+
+fail:
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+	return ret;
 }