[2/2] fetch edid for mac mini through CRT DDC
diff mbox

Message ID 20090804092022.GA9260@mac.home
State Rejected
Headers show

Commit Message

Tino Keitel Aug. 4, 2009, 9:20 a.m. UTC
On Mon, Jun 29, 2009 at 23:30:53 +0800, ling.ma@intel.com wrote:
> For Mac mini we will try to fetch EDID through the analog, 
> when we can not do through SDVO DDC. In this patch we always
> will steal CRT DDC only if it is not effective.

This one seems to be for the userspace driver. There is a similar patch
for KMS, submitted by Keith Packard with the subject
"drm/i915: sdvo: check GPIOA for SDVO DDC (vga DDC) when GPIOE fails",
which works for me and also gives me a framebuffer console with the
native LCD resolution.

I just tested the patch from Keith Packard again with 2.6.31-rc5. The
driver now seems to be stable enough to work on my Mac mini so I can
confirm that this patch works for me.

I doesn't apply cleanly to 2.6.31-rc5, so I attached a rebased patch.

Regards,
Tino

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 4f0c309..4f3747e 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -107,6 +107,9 @@  struct intel_sdvo_priv {
 	/* DDC bus used by this SDVO output */
 	uint8_t ddc_bus;
 
+	/* Mac mini hack -- use the same DDC as the analog connector */
+	struct i2c_adapter *analog_ddc_bus;
+
 	int save_sdvo_mult;
 	u16 save_active_outputs;
 	struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
@@ -1435,6 +1438,33 @@  void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
 	intel_sdvo_read_response(intel_output, &response, 2);
 }
 
+static struct drm_connector *
+intel_find_analog_connector(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+	struct intel_output *intel_output;
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		intel_output = to_intel_output(connector);
+		if (intel_output->type == INTEL_OUTPUT_ANALOG)
+			return connector;
+	}
+	return NULL;
+}
+
+static int
+intel_analog_is_connected(struct drm_device *dev)
+{
+	struct drm_connector *analog_connector;
+	analog_connector = intel_find_analog_connector(dev);
+
+	if (!analog_connector)
+		return false;
+	if (analog_connector->funcs->detect(analog_connector) ==
+	    connector_status_disconnected)
+		return false;
+	return true;
+}
+
 static void
 intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
 {
@@ -1444,6 +1474,11 @@  intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
 
 	edid = drm_get_edid(&intel_output->base,
 			    intel_output->ddc_bus);
+	if (edid == NULL &&
+	    sdvo_priv->analog_ddc_bus &&
+	    !intel_analog_is_connected(intel_output->base.dev))
+		edid = drm_get_edid(&intel_output->base,
+				    sdvo_priv->analog_ddc_bus);
 	if (edid != NULL) {
 		sdvo_priv->is_hdmi = drm_detect_hdmi_monitor(edid);
 		kfree(edid);
@@ -1475,31 +1510,33 @@  static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
 static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
 {
 	struct intel_output *intel_output = to_intel_output(connector);
+	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+	int num_modes;
 
 	/* set the bus switch and get the modes */
-	intel_ddc_get_modes(intel_output);
+	num_modes = intel_ddc_get_modes(intel_output);
 
-#if 0
-	struct drm_device *dev = encoder->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	/* Mac mini hack.  On this device, I get DDC through the analog, which
-	 * load-detects as disconnected.  I fail to DDC through the SDVO DDC,
-	 * but it does load-detect as connected.  So, just steal the DDC bits
-	 * from analog when we fail at finding it the right way.
+	/*
+	 * Mac mini hack.  On this device, the DVI-I connector shares one DDC
+	 * link between analog and digital outputs. So, if the regular SDVO
+	 * DDC fails, check to see if the analog output is disconnected, in which
+	 * case we'll look there for the digital DDC data.
 	 */
-	crt = xf86_config->output[0];
-	intel_output = crt->driver_private;
-	if (intel_output->type == I830_OUTPUT_ANALOG &&
-	    crt->funcs->detect(crt) == XF86OutputStatusDisconnected) {
-		I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A");
-		edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus);
-		xf86DestroyI2CBusRec(intel_output->pDDCBus, true, true);
-	}
-	if (edid_mon) {
-		xf86OutputSetEDID(output, edid_mon);
-		modes = xf86OutputGetEDIDModes(output);
+	if (num_modes == 0 &&
+	    sdvo_priv->analog_ddc_bus &&
+	    !intel_analog_is_connected(intel_output->base.dev))
+	{
+		struct i2c_adapter *digital_ddc_bus;
+
+		/* Switch to the analog ddc bus and try that
+		 */
+		digital_ddc_bus = intel_output->ddc_bus;
+		intel_output->ddc_bus = sdvo_priv->analog_ddc_bus;
+
+		(void) intel_ddc_get_modes(intel_output);
+
+		intel_output->ddc_bus = digital_ddc_bus;
 	}
-#endif
 }
 
 /**
@@ -1687,6 +1724,8 @@  static void intel_sdvo_destroy(struct drm_connector *connector)
 	if (sdvo_priv->sdvo_lvds_fixed_mode != NULL)
 		drm_mode_destroy(connector->dev,
 				 sdvo_priv->sdvo_lvds_fixed_mode);
+ 	if (sdvo_priv->analog_ddc_bus)
+ 		intel_i2c_destroy(sdvo_priv->analog_ddc_bus);
 
 	drm_sysfs_connector_remove(connector);
 	drm_connector_cleanup(connector);
@@ -1913,10 +1952,13 @@  bool intel_sdvo_init(struct drm_device *dev, int output_device)
 	}
 
 	/* setup the DDC bus. */
-	if (output_device == SDVOB)
+	if (output_device == SDVOB) {
 		intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "SDVOB DDC BUS");
-	else
+		sdvo_priv->analog_ddc_bus = intel_i2c_create(dev, GPIOA, "SDVOB/VGA DDC BUS");
+	} else {
 		intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "SDVOC DDC BUS");
+		sdvo_priv->analog_ddc_bus = intel_i2c_create(dev, GPIOA, "SDVOC/VGA DDC BUS");
+	}
 
 	if (intel_output->ddc_bus == NULL)
 		goto err_i2c;
@@ -2042,6 +2084,8 @@  bool intel_sdvo_init(struct drm_device *dev, int output_device)
 	return true;
 
 err_i2c:
+	if (sdvo_priv->analog_ddc_bus != NULL)
+		intel_i2c_destroy(sdvo_priv->analog_ddc_bus);
 	if (intel_output->ddc_bus != NULL)
 		intel_i2c_destroy(intel_output->ddc_bus);
 	if (intel_output->i2c_bus != NULL)