]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
drm/I915: Use the CRT DDC to get the EDID for DVI-connector on Mac
authorKeith Packard <keithp@keithp.com>
Fri, 4 Sep 2009 05:07:54 +0000 (13:07 +0800)
committerEric Anholt <eric@anholt.net>
Fri, 4 Sep 2009 20:05:45 +0000 (13:05 -0700)
mac Mini's have a single DDC line on the DVI connector, shared between the
analog link and the digital link. So, if DDC isn't detected on GPIOE (the
usual SDVO DDC link), try GPIOA (the usual VGA DDC link) when there isn't a
VGA monitor connected.

Signed-off-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/intel_sdvo.c

index cabe32df7cd56818d354807a4e5f199ba8cfe144..c585da8d66277d2f051c39a947fa1fc425b2322e 100644 (file)
@@ -127,6 +127,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;
@@ -1496,6 +1499,36 @@ intel_sdvo_multifunc_encoder(struct intel_output *intel_output)
        return (caps > 1);
 }
 
+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;
+}
+
 enum drm_connector_status
 intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)
 {
@@ -1506,6 +1539,15 @@ intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)
 
        edid = drm_get_edid(&intel_output->base,
                            intel_output->ddc_bus);
+
+       /* when there is no edid and no monitor is connected with VGA
+        * port, try to use the CRT ddc to read the EDID for DVI-connector
+        */
+       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) {
                /* Don't report the output as connected if it's a DVI-I
                 * connector with a non-digital EDID coming out.
@@ -1559,31 +1601,32 @@ 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
 }
 
 /*
@@ -1758,6 +1801,8 @@ static void intel_sdvo_destroy(struct drm_connector *connector)
                intel_i2c_destroy(intel_output->i2c_bus);
        if (intel_output->ddc_bus)
                intel_i2c_destroy(intel_output->ddc_bus);
+       if (sdvo_priv->analog_ddc_bus)
+               intel_i2c_destroy(sdvo_priv->analog_ddc_bus);
 
        if (sdvo_priv->sdvo_lvds_fixed_mode != NULL)
                drm_mode_destroy(connector->dev,
@@ -2177,10 +2222,15 @@ 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;
@@ -2248,6 +2298,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)