summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/drm_edid.c16
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c24
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c33
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h4
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c59
-rw-r--r--drivers/platform/x86/apple-gmux.c1
-rw-r--r--include/linux/vga_switcheroo.h5
7 files changed, 116 insertions, 26 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 76810eae934d..ce07e7fadd99 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -83,8 +83,6 @@ struct detailed_mode_closure {
#define LEVEL_GTF2 2
#define LEVEL_CVT 3
-static DEFINE_MUTEX(drm_edid_mutex);
-
static struct edid_quirk {
char vendor[4];
int product_id;
@@ -420,24 +418,14 @@ struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
struct edid *edid = NULL;
- struct pci_dev *pdev = connector->dev->pdev;
- struct pci_dev *active_pdev = NULL;
-
- mutex_lock(&drm_edid_mutex);
- if (pdev) {
- active_pdev = vga_switcheroo_get_active_client();
- if (active_pdev != pdev)
- vga_switcheroo_switch_ddc(pdev);
- }
+ vga_switcheroo_lock_ddc(connector->dev->pdev);
if (drm_probe_ddc(adapter))
edid = (struct edid *)drm_do_get_edid(connector, adapter);
- if (active_pdev && active_pdev != pdev)
- vga_switcheroo_switch_ddc(active_pdev);
+ vga_switcheroo_unlock_ddc(connector->dev->pdev);
- mutex_unlock(&drm_edid_mutex);
return edid;
}
EXPORT_SYMBOL(drm_get_edid);
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index ffbc9156c792..11580d9df68d 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1279,13 +1279,35 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
spin_unlock(&dev->count_lock);
return can_switch;
}
+static void i915_switcheroo_reprobe(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_connector *con;
+ struct intel_encoder *enc;
+ struct intel_dp *dp;
+
+ list_for_each_entry(con, &dev->mode_config.connector_list, head) {
+ if (con->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ enc = intel_attached_encoder(con);
+ dp = container_of(enc, struct intel_dp, base);
+
+ if (dp->delayed_detect) {
+ DRM_DEBUG_KMS("have delayed eDP detect\n");
+ intel_dp_finish_detect(dev, dp, con);
+ dp->delayed_detect = false;
+ }
+ }
+ }
+}
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
.set_gpu_state = i915_switcheroo_set_state,
- .reprobe = NULL,
+ .reprobe = i915_switcheroo_reprobe,
.can_switch = i915_switcheroo_can_switch,
};
+
static int i915_load_modeset_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 088e4c53d07b..753947cc901b 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -38,6 +38,8 @@
#include "i915_drm.h"
#include "i915_drv.h"
+#include <linux/vga_switcheroo.h>
+
#define DP_LINK_STATUS_SIZE 6
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
@@ -665,9 +667,6 @@ intel_dp_i2c_init(struct intel_dp *intel_dp,
intel_dp->adapter.algo_data = &intel_dp->algo;
intel_dp->adapter.dev.parent = &intel_connector->base.kdev;
- ironlake_edp_panel_vdd_on(intel_dp);
- ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
- ironlake_edp_panel_vdd_off(intel_dp, false);
return ret;
}
@@ -2625,18 +2624,35 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
- if (force_bl_on)
- ironlake_edp_backlight_on(intel_dp);
}
+ if (vga_switcheroo_get_client_mux_active(dev->pdev) == VGA_SWITCHEROO_OFF)
+ {
+ DRM_DEBUG_KMS("no mux ownership delay detecting eDP\n");
+ intel_dp->delayed_detect = true;
+ return;
+ }
intel_dp_i2c_init(intel_dp, intel_connector, name);
+ intel_dp_finish_detect(dev, intel_dp, intel_connector);
+}
+void intel_dp_finish_detect(struct drm_device *dev, struct intel_dp *intel_dp,
+ struct intel_connector *intel_connector)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_connector *connector = &intel_connector->base;
+ int ret;
+ ironlake_edp_panel_vdd_on(intel_dp);
+ ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
+ ironlake_edp_panel_vdd_off(intel_dp, false);
if (is_edp(intel_dp)) {
bool ret;
struct edid *edid;
ironlake_edp_panel_vdd_on(intel_dp);
+ vga_switcheroo_lock_ddc(dev->pdev);
ret = intel_dp_get_dpcd(intel_dp);
+ vga_switcheroo_unlock_ddc(dev->pdev);
ironlake_edp_panel_vdd_off(intel_dp, false);
if (ret) {
@@ -2665,14 +2681,14 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
ironlake_edp_panel_vdd_off(intel_dp, false);
}
- intel_encoder->hot_plug = intel_dp_hot_plug;
+ intel_dp->base.hot_plug = intel_dp_hot_plug;
if (is_edp(intel_dp)) {
- dev_priv->int_edp_connector = connector;
+ dev_priv->int_edp_connector = &intel_connector->base;
intel_panel_setup_backlight(dev);
}
- intel_dp_add_properties(intel_dp, connector);
+ intel_dp_add_properties(intel_dp, &intel_connector->base);
/* 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
@@ -2683,3 +2699,4 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
}
+
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 351fd7179cd7..4dc6d79113e9 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -360,6 +360,7 @@ struct intel_dp {
bool want_panel_vdd;
struct edid *edid; /* cached EDID for eDP */
int edid_mode_count;
+ bool delayed_detect;
};
static inline struct drm_crtc *
@@ -579,4 +580,7 @@ extern void intel_ddi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+void intel_dp_finish_detect(struct drm_device *dev, struct intel_dp *intel_dp,
+ struct intel_connector *conn);
+
#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index d5cd2747bdc3..89df582262ba 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -56,6 +56,9 @@ struct vgasr_priv {
struct list_head clients;
struct vga_switcheroo_handler *handler;
+
+ struct mutex ddc_lock;
+ struct pci_dev *old_ddc_owner;
};
#define ID_BIT_AUDIO 0x100
@@ -69,6 +72,7 @@ static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
/* only one switcheroo per system */
static struct vgasr_priv vgasr_priv = {
.clients = LIST_HEAD_INIT(vgasr_priv.clients),
+ .ddc_lock = __MUTEX_INITIALIZER(vgasr_priv.ddc_lock),
};
static BLOCKING_NOTIFIER_HEAD(vga_switcheroo_notifier_list);
@@ -252,6 +256,19 @@ struct pci_dev *vga_switcheroo_get_active_client(void)
}
EXPORT_SYMBOL(vga_switcheroo_get_active_client);
+int vga_switcheroo_get_client_mux_active(struct pci_dev *pdev)
+{
+ struct vga_switcheroo_client *client;
+
+ client = find_client_from_pci(&vgasr_priv.clients, pdev);
+ if (!client)
+ return VGA_SWITCHEROO_NOT_FOUND;
+ if (!vgasr_priv.active)
+ return VGA_SWITCHEROO_INIT;
+ return client->active ? VGA_SWITCHEROO_ON : VGA_SWITCHEROO_OFF;
+}
+EXPORT_SYMBOL(vga_switcheroo_get_client_mux_active);
+
int vga_switcheroo_get_client_state(struct pci_dev *pdev)
{
struct vga_switcheroo_client *client;
@@ -300,8 +317,9 @@ void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
}
EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
-int vga_switcheroo_switch_ddc(struct pci_dev *pdev)
+int vga_switcheroo_lock_ddc(struct pci_dev *pdev)
{
+ struct vga_switcheroo_client *client;
int ret = 0;
int id;
@@ -313,6 +331,18 @@ int vga_switcheroo_switch_ddc(struct pci_dev *pdev)
}
if (vgasr_priv.handler->switch_ddc) {
+ mutex_lock(&vgasr_priv.ddc_lock);
+
+ client = find_active_client(&vgasr_priv.clients);
+ if (!client) {
+ mutex_unlock(&vgasr_priv.ddc_lock);
+ ret = -ENODEV;
+ goto out;
+ }
+ vgasr_priv.old_ddc_owner = client->pdev;
+ if (client->pdev == pdev)
+ goto out;
+
id = vgasr_priv.handler->get_client_id(pdev);
ret = vgasr_priv.handler->switch_ddc(id);
}
@@ -321,7 +351,32 @@ out:
mutex_unlock(&vgasr_mutex);
return ret;
}
-EXPORT_SYMBOL(vga_switcheroo_switch_ddc);
+EXPORT_SYMBOL(vga_switcheroo_lock_ddc);
+
+int vga_switcheroo_unlock_ddc(struct pci_dev *pdev)
+{
+ int ret = 0;
+ int id;
+ mutex_lock(&vgasr_mutex);
+
+ if (!vgasr_priv.handler) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (vgasr_priv.handler->switch_ddc) {
+ if (vgasr_priv.old_ddc_owner != pdev) {
+ id = vgasr_priv.handler->get_client_id(vgasr_priv.old_ddc_owner);
+ ret = vgasr_priv.handler->switch_ddc(id);
+ }
+ vgasr_priv.old_ddc_owner = NULL;
+ mutex_unlock(&vgasr_priv.ddc_lock);
+ }
+out:
+ mutex_unlock(&vgasr_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(vga_switcheroo_unlock_ddc);
static int vga_switcheroo_show(struct seq_file *m, void *v)
{
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 2c765de40eb6..f8c53464a44b 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -273,6 +273,7 @@ static const struct backlight_ops gmux_bl_ops = {
static int gmux_switch_ddc(enum vga_switcheroo_client_id id)
{
+ printk("switching gmux ddc %d\n", id);
if (id == VGA_SWITCHEROO_IGD)
gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
else
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index c3d7c6fd01ca..d2d2be0b9b2b 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -65,7 +65,8 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
void vga_switcheroo_client_fb_set(struct pci_dev *dev,
struct fb_info *info);
-int vga_switcheroo_switch_ddc(struct pci_dev *pdev);
+int vga_switcheroo_lock_ddc(struct pci_dev *pdev);
+int vga_switcheroo_unlock_ddc(struct pci_dev *pdev);
int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler);
void vga_switcheroo_unregister_handler(void);
@@ -74,6 +75,7 @@ int vga_switcheroo_process_delayed_switch(void);
struct pci_dev *vga_switcheroo_get_active_client(void);
int vga_switcheroo_get_client_state(struct pci_dev *dev);
+int vga_switcheroo_get_client_mux_active(struct pci_dev *dev);
#else
@@ -94,6 +96,7 @@ static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
static inline struct pci_dev *vga_switcheroo_get_active_client(void) { return NULL; }
static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
+static inline int vga_switcheroo_get_client_mux_active(struct pci_dev *dev) { return 0; }
#endif
#endif /* _LINUX_VGA_SWITCHEROO_H_ */