Missing amdgpu firmware files
On Tue, Oct 27, 2015 at 04:10:33PM +, Deucher, Alexander wrote: > > -Original Message- > > From: Seth Forshee [mailto:seth.forshee at canonical.com] > > Sent: Tuesday, October 27, 2015 12:06 PM > > To: Koenig, Christian > > Cc: Deucher, Alexander; Oded Gabbay; dri-devel at lists.freedesktop.org > > Subject: Re: Missing amdgpu firmware files > > > > On Tue, Oct 27, 2015 at 04:47:53PM +0100, Christian König wrote: > > > Well that's strange. Essentially those are just duplicates to the > > > existing files and the driver should fallback to them. > > > > > > IIRC the radeon/*_sdma1.bin files are only requested when you try to > > > use CIK support with amdgpu which isn't a good idea in a production > > > environment. > > > > > > What's the link for the bug reports? It's most likely just people > > > trying to use the driver with hardware which it isn't supposed to be > > > used with. > > > > There have been a few reports that the firmware is missing, but only one > > I see where the user actually reports any display problems. > > > > http://bugs.launchpad.net/bugs/1510405 > > > > Of course it's possible that he is jumping to conclusions about the > > missing firmware files causing his display issues. > > The driver refuses to load if the firmware is missing. However, as Christian > mentioned, you will only get those messages if you've enabled CIK support > amdgpu which we don't recommend (controlled by CONFIG_DRM_AMDGPU_CIK). > Radeon does not require the additional firmware. Got it, thanks. I'll disable it. Seth
Missing amdgpu firmware files
On Tue, Oct 27, 2015 at 04:47:53PM +0100, Christian König wrote: > Well that's strange. Essentially those are just duplicates to the > existing files and the driver should fallback to them. > > IIRC the radeon/*_sdma1.bin files are only requested when you try to > use CIK support with amdgpu which isn't a good idea in a production > environment. > > What's the link for the bug reports? It's most likely just people > trying to use the driver with hardware which it isn't supposed to be > used with. There have been a few reports that the firmware is missing, but only one I see where the user actually reports any display problems. http://bugs.launchpad.net/bugs/1510405 Of course it's possible that he is jumping to conclusions about the missing firmware files causing his display issues. Thanks, Seth
Missing amdgpu firmware files
I've received reports from Ubuntu users about missing amdgpu firmware files with 4.2 kernels. The driver calls out a number of radeon/*_sdma1.bin files that are not present in the linux-firmware git repo. Are these files available anywhere (under a license that allows redistribution), and will they be pushed up to linux-firmware anytime soon? Thanks, Seth
Re: [PATCH v5 0/4] Fix Win8 backlight issue
On Sat, Oct 12, 2013 at 04:44:30AM -0700, Josh Boyer wrote: On Fri, Oct 11, 2013 at 4:27 PM, Rafael J. Wysocki r...@rjwysocki.net wrote: On Friday, October 11, 2013 06:01:43 PM Josh Boyer wrote: On Fri, Oct 11, 2013 at 6:10 PM, Rafael J. Wysocki r...@rjwysocki.net wrote: On Friday, October 11, 2013 12:42:43 PM Josh Boyer wrote: On Fri, Oct 11, 2013 at 9:27 AM, Aaron Lu aaron...@intel.com wrote: v5: 1 Introduce video.use_native_backlight module parameter and set its value to false by default as suggested by Rafael. For Win8 systems which have broken ACPI video backlight control, the parameter can be set to 1 in kernel cmdline to skip registering ACPI video's backlight interface. Due to this change, the acpi_video_verify_backlight_support is moved from video_detect.c to video.c - patch 3/4; That's a fairly untenable position for distro kernels to be in. They now have to ask every user that reports an issue with the backlight to try setting that option on the command line. While I appreciate the setting breaks things for some people, doesn't the Win8 issue impact far more people? Shouldn't it be defaulted to true? Well, we have a rule in the kernel not to introduce regressions for users even if they are minority. If nothing else, can you add a config option for the default so distros can use that to decide which way to default it and then work on fixing the remaining users that have troubles? The current plan is to create a blacklist of systems where that option should be set. We actually already have one, but it is at the _OSI() level, which is overkill in my view and may affect things beyond backlight. Along with that we will debug systems where setting that option (to true) causes problems to happen, so that we'll be able to drop it going forward (hopefully). Of course, distro kernels may always change the default to true if they want. They can, but they'd need to either patch the kernel to do so, or code it in userspace bootloader configs. Having a config option they can set to change the default makes it reasonable and contained within the kernel. If we are to use a Kconfig option, why don't we use one instead of rather than in addition to a command line option? Say, we have CONFIG_ACPI_VIDEO_WIN8_WORKAROUND and if that is set, the code will work like the previous version of the Aaron's patchset (the one without video.use_native_backlight)? Opinions? If you only have a config option, users can't override the distro settings. If you simply have a config option for the default value, the distros can set it without having to carry a patch (the primary benefit), but users can still override that without having to rebuild a kernel. It sounds like the blacklist and the default value of the parameter would be inherently tied together, i.e. the blacklist essentially overrides the default value for specific machines. So when the config option were flipped from its default the blacklist wouldn't work anymore, and you'd need a second blacklist for machines which require video.use_native_backlight=n. I doubt anyone wants to see that happen, so I think we have to pick one value or the other for the default and make it configurable only via the command line. Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/3] Fix backlight issues on some Windows 8 systems
On Sun, Jun 09, 2013 at 07:01:36PM -0400, Matthew Garrett wrote: > Windows 8 introduced new policy for backlight control by pushing it out to > graphics drivers. This appears to have coincided with a range of vendors > adding Windows 8 checks to their backlight control code which trigger either > awkward behaviour (Lenovo) or complete brokenness (some Dells). The simplest > thing to do would be to just disable ACPI backlight control entirely if the > firmware indicates Windows 8 support, but it's entirely possible that > individual graphics drivers might still make use of the ACPI functionality in > preference to native control. > > The first two patches in this series are picked from other patchesets aimed at > solving similar problems. The last simply unregisters ACPI backlight control > on Windows 8 systems when using an Intel GPU. Similar code could be added to > other drivers, but I'm reluctant to do so without further investigation as > to the behaviour of the vendor drivers under Windows. I've received some feedback from user testing of these patches (don't have an affected machine myself) and the feedback is mostly good. One user reported it didn't work, but I that machine may have discrete graphics (waiting to hear back from the user). The main drawback I see with any approach like this one is that the backlight remains broken for users of proprietary graphics drivers. Seth
Re: [PATCH 0/3] Fix backlight issues on some Windows 8 systems
On Sun, Jun 09, 2013 at 07:01:36PM -0400, Matthew Garrett wrote: Windows 8 introduced new policy for backlight control by pushing it out to graphics drivers. This appears to have coincided with a range of vendors adding Windows 8 checks to their backlight control code which trigger either awkward behaviour (Lenovo) or complete brokenness (some Dells). The simplest thing to do would be to just disable ACPI backlight control entirely if the firmware indicates Windows 8 support, but it's entirely possible that individual graphics drivers might still make use of the ACPI functionality in preference to native control. The first two patches in this series are picked from other patchesets aimed at solving similar problems. The last simply unregisters ACPI backlight control on Windows 8 systems when using an Intel GPU. Similar code could be added to other drivers, but I'm reluctant to do so without further investigation as to the behaviour of the vendor drivers under Windows. I've received some feedback from user testing of these patches (don't have an affected machine myself) and the feedback is mostly good. One user reported it didn't work, but I that machine may have discrete graphics (waiting to hear back from the user). The main drawback I see with any approach like this one is that the backlight remains broken for users of proprietary graphics drivers. Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/7] Fixes for hybrid graphics Apple machines
Many hybrid graphics Apple laptops fail to set up LVDS on the secondary GPU due to missing or incorrect mode information for the panel at init time. The only way to get the LVDS mode on these machines is via the DDC, but this is muxed to the active GPU at boot. However, the graphics mux on these machines supports muxing the i2c idependently of the display, making it possible for the secondary graphics driver to read the EDID without a full display switch. In order to support this, these patches modify vga_switcheroo to allow muxing of the DDC idependently of the display. apple-gmux is updated to support this new functionality, and drm_get_edid() is modified to switch the DDC mux as needed. For this to work we also need to ensure that sufficient switcheroo suport is available before initializing the secondary GPU. This is done by adding any non-active GPUs that try to initialize before switcheroo is ready to a list and initializing these devices once switcheroo becomes ready. This behavior is restricted to Apple laptops to prevent causing problems on other machines. Thanks, Seth Seth Forshee (7): vga_switcheroo: Add support for switching only the DDC vga_switcheroo: Add helper function to get the active client vga_switcheroo: Add notifier call chain for switcheroo events apple-gmux: Add switch_ddc support drm/edid: Switch DDC when reading the EDID drm/pci: Add drm_put_pci_dev() drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready drivers/gpu/drm/ast/ast_drv.c |2 +- drivers/gpu/drm/cirrus/cirrus_drv.c |2 +- drivers/gpu/drm/drm_drv.c |3 + drivers/gpu/drm/drm_edid.c| 17 drivers/gpu/drm/drm_pci.c | 172 + drivers/gpu/drm/gma500/psb_drv.c |2 +- drivers/gpu/drm/i915/i915_drv.c |2 +- drivers/gpu/drm/mgag200/mgag200_drv.c |2 +- drivers/gpu/drm/nouveau/nouveau_drv.c |2 +- drivers/gpu/drm/radeon/radeon_drv.c |2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c |2 +- drivers/gpu/vga/vga_switcheroo.c | 87 - drivers/platform/x86/apple-gmux.c | 12 ++- include/drm/drmP.h|3 + include/linux/vga_switcheroo.h| 20 15 files changed, 302 insertions(+), 28 deletions(-) ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/7] vga_switcheroo: Add support for switching only the DDC
During graphics driver initialization its useful to be able to mux only the DDC to the inactive client in order to read the EDID. Add a switch_ddc callback to allow capable handlers to provide this functionality, and add vga_switcheroo_switch_ddc() to allow DRM to mux only the DDC. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 39 +- include/linux/vga_switcheroo.h |4 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..ea6bcc2 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -252,6 +252,29 @@ 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 ret = 0; + int id; + + mutex_lock(vgasr_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler-switch_ddc) { + id = vgasr_priv.handler-get_client_id(pdev); + ret = vgasr_priv.handler-switch_ddc(id); + } + +out: + mutex_unlock(vgasr_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +365,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, event); } + if (vgasr_priv.handler-switch_ddc) { + ret = vgasr_priv.handler-switch_ddc(new_client-id); + if (ret) + return ret; + } + ret = vgasr_priv.handler-switchto(new_client-id); if (ret) - return ret; + goto restore_ddc; if (new_client-ops-reprobe) new_client-ops-reprobe(new_client-pdev); @@ -356,6 +385,14 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) new_client-active = true; return 0; + +restore_ddc: + if (vgasr_priv.handler-switch_ddc) { + int id = (new_client-id == VGA_SWITCHEROO_IGD) ? + VGA_SWITCHEROO_DIS : VGA_SWITCHEROO_IGD; + ret = vgasr_priv.handler-switch_ddc(id); + } + return ret; } static bool check_can_switch(void) diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index ddb419c..b0d0839 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -29,6 +29,7 @@ enum vga_switcheroo_client_id { }; struct vga_switcheroo_handler { + int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); int (*power_state)(enum vga_switcheroo_client_id id, enum vga_switcheroo_state state); @@ -53,6 +54,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_register_handler(struct vga_switcheroo_handler *handler); void vga_switcheroo_unregister_handler(void); @@ -66,6 +69,7 @@ static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} +static inline void vga_switcheroo_switch_ddc(struct pci_dev *pdev) { return NULL; } static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 3/7] vga_switcheroo: Add notifier call chain for switcheroo events
DRM needs to be notified of client and handler registration in order to defer initialization of the secondary GPU until the EDID can be read from the LVDS panel. To support this add a notifier call chain to vga_switcheroo for subscribing to switcheroo events. Events are generated for registration and unregistration of handlers and clients. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 34 ++ include/linux/vga_switcheroo.h | 14 ++ 2 files changed, 48 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e53f67d..d5cd274 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -24,6 +24,7 @@ #include linux/fs.h #include linux/debugfs.h #include linux/fb.h +#include linux/notifier.h #include linux/pci.h #include linux/vga_switcheroo.h @@ -70,6 +71,28 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; +static BLOCKING_NOTIFIER_HEAD(vga_switcheroo_notifier_list); + +int vga_switcheroo_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(vga_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_register_notifier); + +int vga_switcheroo_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(vga_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_notifier); + +static int vga_switcheroo_notifier_call_chain(enum vga_switcheroo_event event) +{ + return blocking_notifier_call_chain(vga_switcheroo_notifier_list, + event, NULL); +} + static bool vga_switcheroo_ready(void) { /* we're ready if we get two clients + handler */ @@ -113,10 +136,18 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) vga_switcheroo_enable(); } mutex_unlock(vgasr_mutex); + + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_REGISTERED); return 0; } EXPORT_SYMBOL(vga_switcheroo_register_handler); +bool vga_switcheroo_handler_registered(void) +{ + return !!vgasr_priv.handler; +} +EXPORT_SYMBOL(vga_switcheroo_handler_registered); + void vga_switcheroo_unregister_handler(void) { mutex_lock(vgasr_mutex); @@ -127,6 +158,7 @@ void vga_switcheroo_unregister_handler(void) vgasr_priv.active = false; } mutex_unlock(vgasr_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_handler); @@ -156,6 +188,7 @@ static int register_client(struct pci_dev *pdev, vga_switcheroo_enable(); } mutex_unlock(vgasr_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_REGISTERED); return 0; } @@ -250,6 +283,7 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev) vgasr_priv.active = false; } mutex_unlock(vgasr_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_client); diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index e361858..c3d7c6f 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -11,6 +11,7 @@ #define _LINUX_VGA_SWITCHEROO_H_ #include linux/fb.h +#include linux/notifier.h struct pci_dev; @@ -28,6 +29,13 @@ enum vga_switcheroo_client_id { VGA_SWITCHEROO_MAX_CLIENTS, }; +enum vga_switcheroo_event { + VGA_SWITCHEROO_CLIENT_REGISTERED, + VGA_SWITCHEROO_CLIENT_UNREGISTERED, + VGA_SWITCHEROO_HANDLER_REGISTERED, + VGA_SWITCHEROO_HANDLER_UNREGISTERED, +}; + struct vga_switcheroo_handler { int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); @@ -44,6 +52,9 @@ struct vga_switcheroo_client_ops { }; #if defined(CONFIG_VGA_SWITCHEROO) +int vga_switcheroo_register_notifier(struct notifier_block *nb); +int vga_switcheroo_unregister_notifier(struct notifier_block *nb); +bool vga_switcheroo_handler_registered(void); void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops); @@ -66,6 +77,9 @@ int vga_switcheroo_get_client_state(struct pci_dev *dev); #else +static inline int vga_switcheroo_register_notifier(struct notifier_block *nb) { return 0; } +static inline int vga_switcheroo_unregister_notifier(struct notifier_block *nb) { return 0; } +static inline bool vga_switcheroo_handler_registered(void) { return false; } static inline void
[PATCH 4/7] apple-gmux: Add switch_ddc support
The gmux allows muxing the DDC independently from the display, so support this functionality. This will allow reading the EDID for the inactive GPU, fixing issues with machines that either don't have a VBT or have invalid mode data in the VBT. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/platform/x86/apple-gmux.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index dfb1a92..d1e372d 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -269,14 +269,21 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switch_ddc(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + else + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + return 0; +} + static int gmux_switchto(enum vga_switcheroo_client_id id) { if (id == VGA_SWITCHEROO_IGD) { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); } else { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); } @@ -343,6 +350,7 @@ gmux_active_client(struct apple_gmux_data *gmux_data) } static struct vga_switcheroo_handler gmux_handler = { + .switch_ddc = gmux_switch_ddc, .switchto = gmux_switchto, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 5/7] drm/edid: Switch DDC when reading the EDID
Some dual graphics machines support muxing the DDC separately from the display, so make use of this functionality when reading the EDID on the inactive GPU. Also serialize drm_get_edid() with a mutex to avoid races on the DDC mux state. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/drm_edid.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b7ee230..b389269 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include linux/slab.h #include linux/i2c.h #include linux/module.h +#include linux/vga_switcheroo.h #include drmP.h #include drm_edid.h #include drm_edid_modes.h @@ -82,6 +83,8 @@ 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; @@ -398,12 +401,26 @@ 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); + } 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); + connector-display_info.raw_edid = (char *)edid; + mutex_unlock(drm_edid_mutex); return edid; } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 6/7] drm/pci: Add drm_put_pci_dev()
When deferred initialization support for pci devices is added some additional cleanup will be needed. Add a pci-specific put function to serve this purpose, and convert the pci drivers over to using it. For now it just calls drm_put_dev(), so this commit has no functional change. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/ast/ast_drv.c |2 +- drivers/gpu/drm/cirrus/cirrus_drv.c |2 +- drivers/gpu/drm/drm_pci.c |8 +++- drivers/gpu/drm/gma500/psb_drv.c |2 +- drivers/gpu/drm/i915/i915_drv.c |2 +- drivers/gpu/drm/mgag200/mgag200_drv.c |2 +- drivers/gpu/drm/nouveau/nouveau_drv.c |2 +- drivers/gpu/drm/radeon/radeon_drv.c |2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c |2 +- include/drm/drmP.h|1 + 10 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index d0c4574..001298d 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -72,7 +72,7 @@ ast_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 7053140..c7ca02b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -64,7 +64,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations cirrus_driver_fops = { diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 5320364..55eb824 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -388,6 +388,12 @@ err_g1: } EXPORT_SYMBOL(drm_get_pci_dev); +void drm_put_pci_dev(struct drm_device *dev) +{ + drm_put_dev(dev); +} +EXPORT_SYMBOL(drm_put_pci_dev); + /** * PCI device initialization. Called direct from modules at load time. * @@ -460,7 +466,7 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) pci_unregister_driver(pdriver); } else { list_for_each_entry_safe(dev, tmp, driver-device_list, driver_item) - drm_put_dev(dev); + drm_put_pci_dev(dev); } DRM_INFO(Module unloaded\n); } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0c47374..d7c3c9c 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -585,7 +585,7 @@ static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) static void psb_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct dev_pm_ops psb_pm_ops = { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a24ffbe..86ae5a2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -856,7 +856,7 @@ i915_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int i915_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index ea1024d..a3b0a4a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -73,7 +73,7 @@ static void mga_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations mgag200_driver_fops = { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 9a36f5f..b74b02a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -168,7 +168,7 @@ nouveau_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } int diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8c593ea..05d2ebc 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -310,7 +310,7 @@ radeon_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4d9edea..cf901cc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -982,7 +982,7 @@ static void vmw_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev
[PATCH 7/7] drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready
Many Apple laptops with hybrid graphics require switching the i2c mux to the integrated GPU when that device is being initialized in order to get correct mode information for the LVDS panel. This requires that switcheroo is ready at the time the device is initialized, which is not guaranteed. To support this, delay calling the driver load() callback until the vga_switcheroo handler and active client have been registered. This is restricted to Apple notebooks via DMI data to avoid causing problems on machines without switcheroo support. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/drm_drv.c |3 + drivers/gpu/drm/drm_pci.c | 164 - include/drm/drmP.h|2 + 3 files changed, 153 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9238de4..124fd8a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -276,6 +276,8 @@ static int __init drm_core_init(void) goto err_p3; } + drm_pci_module_init(); + DRM_INFO(Initialized %s %d.%d.%d %s\n, CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); return 0; @@ -291,6 +293,7 @@ err_p1: static void __exit drm_core_exit(void) { + drm_pci_module_exit(); remove_proc_entry(dri, NULL); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 55eb824..a5c9068 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -40,6 +40,10 @@ #include linux/slab.h #include linux/dma-mapping.h #include linux/export.h +#include linux/dmi.h +#include linux/notifier.h +#include linux/vgaarb.h +#include linux/vga_switcheroo.h #include drmP.h /**/ @@ -297,19 +301,8 @@ static struct drm_bus drm_pci_bus = { .agp_init = drm_pci_agp_init, }; -/** - * Register. - * - * \param pdev - PCI device structure - * \param ent entry from the PCI ID table with device type flags - * \return zero on success or a negative number on failure. - * - * Attempt to gets inter module drm information. If we are first - * then register the character device and inter module information. - * Try and register, if we fail to register, backout previous work. - */ -int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, - struct drm_driver *driver) +int __drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, + struct drm_driver *driver) { struct drm_device *dev; int ret; @@ -334,8 +327,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, dev-hose = pdev-sysdata; #endif - mutex_lock(drm_global_mutex); - if ((ret = drm_fill_in_dev(dev, ent, driver))) { printk(KERN_ERR DRM: Fill_in_dev failed.\n); goto err_g2; @@ -371,7 +362,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, driver-name, driver-major, driver-minor, driver-patchlevel, driver-date, pci_name(pdev), dev-primary-index); - mutex_unlock(drm_global_mutex); return 0; err_g4: @@ -386,10 +376,140 @@ err_g1: mutex_unlock(drm_global_mutex); return ret; } + +/* + * List of machines that require delaying initialization of the secondary + * GPU until vga_switcheroo is ready. + */ +static struct dmi_system_id deferred_init_dmi_table[] = { + { + .ident = Apple Laptop, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, Apple Inc.), + DMI_MATCH(DMI_CHASSIS_TYPE, 10), /* Notebook */ + }, + }, +}; + +struct deferred_init_data { + struct list_head list; + struct pci_dev *pdev; + const struct pci_device_id *ent; + struct drm_driver *driver; +}; + +static LIST_HEAD(deferred_init_list); + +static bool drm_pci_switcheroo_ready(void) +{ + if (!vga_switcheroo_handler_registered()) + return false; + if (!vga_switcheroo_get_active_client()) + return false; + return true; +} + +static void drm_deferred_init_work_fn(struct work_struct *work) +{ + struct deferred_init_data *di_data, *temp; + + mutex_lock(drm_global_mutex); + + if (!drm_pci_switcheroo_ready()) { + mutex_unlock(drm_global_mutex); + return; + } + + list_for_each_entry_safe(di_data, temp, deferred_init_list, list) { + if (__drm_get_pci_dev(di_data-pdev, di_data-ent, + di_data-driver)) + DRM_ERROR(pci device initialization failed\n); + list_del(di_data-list); + kfree(di_data); + } + mutex_unlock
Re: [PATCH 0/7] Fixes for hybrid graphics Apple machines
On Fri, Sep 07, 2012 at 10:35:04PM +0100, Dave Airlie wrote: On Fri, Sep 7, 2012 at 4:22 PM, Seth Forshee seth.fors...@canonical.com wrote: Many hybrid graphics Apple laptops fail to set up LVDS on the secondary GPU due to missing or incorrect mode information for the panel at init time. The only way to get the LVDS mode on these machines is via the DDC, but this is muxed to the active GPU at boot. However, the graphics mux on these machines supports muxing the i2c idependently of the display, making it possible for the secondary graphics driver to read the EDID without a full display switch. In order to support this, these patches modify vga_switcheroo to allow muxing of the DDC idependently of the display. apple-gmux is updated to support this new functionality, and drm_get_edid() is modified to switch the DDC mux as needed. For this to work we also need to ensure that sufficient switcheroo suport is available before initializing the secondary GPU. This is done by adding any non-active GPUs that try to initialize before switcheroo is ready to a list and initializing these devices once switcheroo becomes ready. This behavior is restricted to Apple laptops to prevent causing problems on other machines. I hate this idea, no delaying stuff. We either need to have some sort of enforced ordering or make some stuff only work built-in. But sticking things on a delayed list just in case seems wrong to me. I really don't like it either. My preferred solution would be to have i915 register the LVDS connector whenever we can reasonably expect that an LVDS panel might be present, then treat the panel as disconnected until we can get the mode information. I was headed down this path with the first patches I sent, but both Daniel and Matthew responded unfavorably to this approach. Otherwise I'm open to suggestions on how enforcing the ordering of the device initializations. Forcing apple-gmux to be built-in or making i915 depend on it might solve the problem, but I don't find either of these to be very desirable either. Thanks, Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/7] Fixes for hybrid graphics Apple machines
On Fri, Sep 07, 2012 at 10:35:04PM +0100, Dave Airlie wrote: > On Fri, Sep 7, 2012 at 4:22 PM, Seth Forshee > wrote: > > Many hybrid graphics Apple laptops fail to set up LVDS on the secondary > > GPU due to missing or incorrect mode information for the panel at init > > time. The only way to get the LVDS mode on these machines is via the > > DDC, but this is muxed to the active GPU at boot. However, the graphics > > mux on these machines supports muxing the i2c idependently of the > > display, making it possible for the secondary graphics driver to read > > the EDID without a full display switch. > > > > In order to support this, these patches modify vga_switcheroo to allow > > muxing of the DDC idependently of the display. apple-gmux is updated to > > support this new functionality, and drm_get_edid() is modified to switch > > the DDC mux as needed. > > > > For this to work we also need to ensure that sufficient switcheroo > > suport is available before initializing the secondary GPU. This is done > > by adding any non-active GPUs that try to initialize before switcheroo > > is ready to a list and initializing these devices once switcheroo > > becomes ready. This behavior is restricted to Apple laptops to prevent > > causing problems on other machines. > > I hate this idea, no delaying stuff. We either need to have some sort > of enforced ordering or make some stuff only work built-in. > > But sticking things on a delayed list just in case seems wrong to me. I really don't like it either. My preferred solution would be to have i915 register the LVDS connector whenever we can reasonably expect that an LVDS panel might be present, then treat the panel as disconnected until we can get the mode information. I was headed down this path with the first patches I sent, but both Daniel and Matthew responded unfavorably to this approach. Otherwise I'm open to suggestions on how enforcing the ordering of the device initializations. Forcing apple-gmux to be built-in or making i915 depend on it might solve the problem, but I don't find either of these to be very desirable either. Thanks, Seth
[PATCH 7/7] drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready
Many Apple laptops with hybrid graphics require switching the i2c mux to the integrated GPU when that device is being initialized in order to get correct mode information for the LVDS panel. This requires that switcheroo is ready at the time the device is initialized, which is not guaranteed. To support this, delay calling the driver load() callback until the vga_switcheroo handler and active client have been registered. This is restricted to Apple notebooks via DMI data to avoid causing problems on machines without switcheroo support. Signed-off-by: Seth Forshee --- drivers/gpu/drm/drm_drv.c |3 + drivers/gpu/drm/drm_pci.c | 164 - include/drm/drmP.h|2 + 3 files changed, 153 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9238de4..124fd8a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -276,6 +276,8 @@ static int __init drm_core_init(void) goto err_p3; } + drm_pci_module_init(); + DRM_INFO("Initialized %s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); return 0; @@ -291,6 +293,7 @@ err_p1: static void __exit drm_core_exit(void) { + drm_pci_module_exit(); remove_proc_entry("dri", NULL); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 55eb824..a5c9068 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -40,6 +40,10 @@ #include #include #include +#include +#include +#include +#include #include "drmP.h" /**/ @@ -297,19 +301,8 @@ static struct drm_bus drm_pci_bus = { .agp_init = drm_pci_agp_init, }; -/** - * Register. - * - * \param pdev - PCI device structure - * \param ent entry from the PCI ID table with device type flags - * \return zero on success or a negative number on failure. - * - * Attempt to gets inter module "drm" information. If we are first - * then register the character device and inter module information. - * Try and register, if we fail to register, backout previous work. - */ -int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, - struct drm_driver *driver) +int __drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, + struct drm_driver *driver) { struct drm_device *dev; int ret; @@ -334,8 +327,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, dev->hose = pdev->sysdata; #endif - mutex_lock(_global_mutex); - if ((ret = drm_fill_in_dev(dev, ent, driver))) { printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); goto err_g2; @@ -371,7 +362,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, driver->name, driver->major, driver->minor, driver->patchlevel, driver->date, pci_name(pdev), dev->primary->index); - mutex_unlock(_global_mutex); return 0; err_g4: @@ -386,10 +376,140 @@ err_g1: mutex_unlock(_global_mutex); return ret; } + +/* + * List of machines that require delaying initialization of the secondary + * GPU until vga_switcheroo is ready. + */ +static struct dmi_system_id deferred_init_dmi_table[] = { + { + .ident = "Apple Laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, +}; + +struct deferred_init_data { + struct list_head list; + struct pci_dev *pdev; + const struct pci_device_id *ent; + struct drm_driver *driver; +}; + +static LIST_HEAD(deferred_init_list); + +static bool drm_pci_switcheroo_ready(void) +{ + if (!vga_switcheroo_handler_registered()) + return false; + if (!vga_switcheroo_get_active_client()) + return false; + return true; +} + +static void drm_deferred_init_work_fn(struct work_struct *work) +{ + struct deferred_init_data *di_data, *temp; + + mutex_lock(_global_mutex); + + if (!drm_pci_switcheroo_ready()) { + mutex_unlock(_global_mutex); + return; + } + + list_for_each_entry_safe(di_data, temp, _init_list, list) { + if (__drm_get_pci_dev(di_data->pdev, di_data->ent, + di_data->driver)) + DRM_ERROR("pci device initialization failed\n"); + list_del(_data->list); + kfree(di_data); + } + mutex_unlock(_globa
[PATCH 6/7] drm/pci: Add drm_put_pci_dev()
When deferred initialization support for pci devices is added some additional cleanup will be needed. Add a pci-specific put function to serve this purpose, and convert the pci drivers over to using it. For now it just calls drm_put_dev(), so this commit has no functional change. Signed-off-by: Seth Forshee --- drivers/gpu/drm/ast/ast_drv.c |2 +- drivers/gpu/drm/cirrus/cirrus_drv.c |2 +- drivers/gpu/drm/drm_pci.c |8 +++- drivers/gpu/drm/gma500/psb_drv.c |2 +- drivers/gpu/drm/i915/i915_drv.c |2 +- drivers/gpu/drm/mgag200/mgag200_drv.c |2 +- drivers/gpu/drm/nouveau/nouveau_drv.c |2 +- drivers/gpu/drm/radeon/radeon_drv.c |2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c |2 +- include/drm/drmP.h|1 + 10 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index d0c4574..001298d 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -72,7 +72,7 @@ ast_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 7053140..c7ca02b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -64,7 +64,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations cirrus_driver_fops = { diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 5320364..55eb824 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -388,6 +388,12 @@ err_g1: } EXPORT_SYMBOL(drm_get_pci_dev); +void drm_put_pci_dev(struct drm_device *dev) +{ + drm_put_dev(dev); +} +EXPORT_SYMBOL(drm_put_pci_dev); + /** * PCI device initialization. Called direct from modules at load time. * @@ -460,7 +466,7 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) pci_unregister_driver(pdriver); } else { list_for_each_entry_safe(dev, tmp, >device_list, driver_item) - drm_put_dev(dev); + drm_put_pci_dev(dev); } DRM_INFO("Module unloaded\n"); } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0c47374..d7c3c9c 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -585,7 +585,7 @@ static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) static void psb_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct dev_pm_ops psb_pm_ops = { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a24ffbe..86ae5a2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -856,7 +856,7 @@ i915_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int i915_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index ea1024d..a3b0a4a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -73,7 +73,7 @@ static void mga_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations mgag200_driver_fops = { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 9a36f5f..b74b02a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -168,7 +168,7 @@ nouveau_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } int diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8c593ea..05d2ebc 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -310,7 +310,7 @@ radeon_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4d9edea..cf901cc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -982,7 +982,7 @@ static void vmw_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - dr
[PATCH 5/7] drm/edid: Switch DDC when reading the EDID
Some dual graphics machines support muxing the DDC separately from the display, so make use of this functionality when reading the EDID on the inactive GPU. Also serialize drm_get_edid() with a mutex to avoid races on the DDC mux state. Signed-off-by: Seth Forshee --- drivers/gpu/drm/drm_edid.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b7ee230..b389269 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" @@ -82,6 +83,8 @@ 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; @@ -398,12 +401,26 @@ 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(_edid_mutex); + + if (pdev) { + active_pdev = vga_switcheroo_get_active_client(); + if (active_pdev != pdev) + vga_switcheroo_switch_ddc(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); + connector->display_info.raw_edid = (char *)edid; + mutex_unlock(_edid_mutex); return edid; } -- 1.7.9.5
[PATCH 4/7] apple-gmux: Add switch_ddc support
The gmux allows muxing the DDC independently from the display, so support this functionality. This will allow reading the EDID for the inactive GPU, fixing issues with machines that either don't have a VBT or have invalid mode data in the VBT. Signed-off-by: Seth Forshee --- drivers/platform/x86/apple-gmux.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index dfb1a92..d1e372d 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -269,14 +269,21 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switch_ddc(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + else + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + return 0; +} + static int gmux_switchto(enum vga_switcheroo_client_id id) { if (id == VGA_SWITCHEROO_IGD) { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); } else { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); } @@ -343,6 +350,7 @@ gmux_active_client(struct apple_gmux_data *gmux_data) } static struct vga_switcheroo_handler gmux_handler = { + .switch_ddc = gmux_switch_ddc, .switchto = gmux_switchto, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, -- 1.7.9.5
[PATCH 3/7] vga_switcheroo: Add notifier call chain for switcheroo events
DRM needs to be notified of client and handler registration in order to defer initialization of the secondary GPU until the EDID can be read from the LVDS panel. To support this add a notifier call chain to vga_switcheroo for subscribing to switcheroo events. Events are generated for registration and unregistration of handlers and clients. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 34 ++ include/linux/vga_switcheroo.h | 14 ++ 2 files changed, 48 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e53f67d..d5cd274 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,28 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; +static BLOCKING_NOTIFIER_HEAD(vga_switcheroo_notifier_list); + +int vga_switcheroo_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_register_notifier); + +int vga_switcheroo_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_notifier); + +static int vga_switcheroo_notifier_call_chain(enum vga_switcheroo_event event) +{ + return blocking_notifier_call_chain(_switcheroo_notifier_list, + event, NULL); +} + static bool vga_switcheroo_ready(void) { /* we're ready if we get two clients + handler */ @@ -113,10 +136,18 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) vga_switcheroo_enable(); } mutex_unlock(_mutex); + + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_REGISTERED); return 0; } EXPORT_SYMBOL(vga_switcheroo_register_handler); +bool vga_switcheroo_handler_registered(void) +{ + return !!vgasr_priv.handler; +} +EXPORT_SYMBOL(vga_switcheroo_handler_registered); + void vga_switcheroo_unregister_handler(void) { mutex_lock(_mutex); @@ -127,6 +158,7 @@ void vga_switcheroo_unregister_handler(void) vgasr_priv.active = false; } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_handler); @@ -156,6 +188,7 @@ static int register_client(struct pci_dev *pdev, vga_switcheroo_enable(); } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_REGISTERED); return 0; } @@ -250,6 +283,7 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev) vgasr_priv.active = false; } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_client); diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index e361858..c3d7c6f 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -11,6 +11,7 @@ #define _LINUX_VGA_SWITCHEROO_H_ #include +#include struct pci_dev; @@ -28,6 +29,13 @@ enum vga_switcheroo_client_id { VGA_SWITCHEROO_MAX_CLIENTS, }; +enum vga_switcheroo_event { + VGA_SWITCHEROO_CLIENT_REGISTERED, + VGA_SWITCHEROO_CLIENT_UNREGISTERED, + VGA_SWITCHEROO_HANDLER_REGISTERED, + VGA_SWITCHEROO_HANDLER_UNREGISTERED, +}; + struct vga_switcheroo_handler { int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); @@ -44,6 +52,9 @@ struct vga_switcheroo_client_ops { }; #if defined(CONFIG_VGA_SWITCHEROO) +int vga_switcheroo_register_notifier(struct notifier_block *nb); +int vga_switcheroo_unregister_notifier(struct notifier_block *nb); +bool vga_switcheroo_handler_registered(void); void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops); @@ -66,6 +77,9 @@ int vga_switcheroo_get_client_state(struct pci_dev *dev); #else +static inline int vga_switcheroo_register_notifier(struct notifier_block *nb) { return 0; } +static inline int vga_switcheroo_unregister_notifier(struct notifier_block *nb) { return 0; } +static inline bool vga_switcheroo_handler_registered(void) { return false; } static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0
[PATCH 2/7] vga_switcheroo: Add helper function to get the active client
Add vga_switcheroo_get_active_client() to allow drivers to get the active video client. This will be used by drivers wishing to temporarily mux only the DDC to the inactive client. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 14 ++ include/linux/vga_switcheroo.h |2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ea6bcc2..e53f67d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -205,6 +205,20 @@ find_active_client(struct list_head *head) return NULL; } +struct pci_dev *vga_switcheroo_get_active_client(void) +{ + struct vga_switcheroo_client *client; + struct pci_dev *pdev = NULL; + + mutex_lock(_mutex); + client = find_active_client(_priv.clients); + if (client) + pdev = client->pdev; + mutex_unlock(_mutex); + return pdev; +} +EXPORT_SYMBOL(vga_switcheroo_get_active_client); + int vga_switcheroo_get_client_state(struct pci_dev *pdev) { struct vga_switcheroo_client *client; diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index b0d0839..e361858 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -61,6 +61,7 @@ void vga_switcheroo_unregister_handler(void); 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); #else @@ -76,6 +77,7 @@ static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, int id, bool active) { return 0; } static inline void vga_switcheroo_unregister_handler(void) {} 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; } -- 1.7.9.5
[PATCH 1/7] vga_switcheroo: Add support for switching only the DDC
During graphics driver initialization its useful to be able to mux only the DDC to the inactive client in order to read the EDID. Add a switch_ddc callback to allow capable handlers to provide this functionality, and add vga_switcheroo_switch_ddc() to allow DRM to mux only the DDC. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 39 +- include/linux/vga_switcheroo.h |4 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..ea6bcc2 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -252,6 +252,29 @@ 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 ret = 0; + int id; + + mutex_lock(_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler->switch_ddc) { + id = vgasr_priv.handler->get_client_id(pdev); + ret = vgasr_priv.handler->switch_ddc(id); + } + +out: + mutex_unlock(_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +365,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, ); } + if (vgasr_priv.handler->switch_ddc) { + ret = vgasr_priv.handler->switch_ddc(new_client->id); + if (ret) + return ret; + } + ret = vgasr_priv.handler->switchto(new_client->id); if (ret) - return ret; + goto restore_ddc; if (new_client->ops->reprobe) new_client->ops->reprobe(new_client->pdev); @@ -356,6 +385,14 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) new_client->active = true; return 0; + +restore_ddc: + if (vgasr_priv.handler->switch_ddc) { + int id = (new_client->id == VGA_SWITCHEROO_IGD) ? + VGA_SWITCHEROO_DIS : VGA_SWITCHEROO_IGD; + ret = vgasr_priv.handler->switch_ddc(id); + } + return ret; } static bool check_can_switch(void) diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index ddb419c..b0d0839 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -29,6 +29,7 @@ enum vga_switcheroo_client_id { }; struct vga_switcheroo_handler { + int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); int (*power_state)(enum vga_switcheroo_client_id id, enum vga_switcheroo_state state); @@ -53,6 +54,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_register_handler(struct vga_switcheroo_handler *handler); void vga_switcheroo_unregister_handler(void); @@ -66,6 +69,7 @@ static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} +static inline void vga_switcheroo_switch_ddc(struct pci_dev *pdev) { return NULL; } static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, -- 1.7.9.5
[PATCH 0/7] Fixes for hybrid graphics Apple machines
Many hybrid graphics Apple laptops fail to set up LVDS on the secondary GPU due to missing or incorrect mode information for the panel at init time. The only way to get the LVDS mode on these machines is via the DDC, but this is muxed to the active GPU at boot. However, the graphics mux on these machines supports muxing the i2c idependently of the display, making it possible for the secondary graphics driver to read the EDID without a full display switch. In order to support this, these patches modify vga_switcheroo to allow muxing of the DDC idependently of the display. apple-gmux is updated to support this new functionality, and drm_get_edid() is modified to switch the DDC mux as needed. For this to work we also need to ensure that sufficient switcheroo suport is available before initializing the secondary GPU. This is done by adding any non-active GPUs that try to initialize before switcheroo is ready to a list and initializing these devices once switcheroo becomes ready. This behavior is restricted to Apple laptops to prevent causing problems on other machines. Thanks, Seth Seth Forshee (7): vga_switcheroo: Add support for switching only the DDC vga_switcheroo: Add helper function to get the active client vga_switcheroo: Add notifier call chain for switcheroo events apple-gmux: Add switch_ddc support drm/edid: Switch DDC when reading the EDID drm/pci: Add drm_put_pci_dev() drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready drivers/gpu/drm/ast/ast_drv.c |2 +- drivers/gpu/drm/cirrus/cirrus_drv.c |2 +- drivers/gpu/drm/drm_drv.c |3 + drivers/gpu/drm/drm_edid.c| 17 drivers/gpu/drm/drm_pci.c | 172 + drivers/gpu/drm/gma500/psb_drv.c |2 +- drivers/gpu/drm/i915/i915_drv.c |2 +- drivers/gpu/drm/mgag200/mgag200_drv.c |2 +- drivers/gpu/drm/nouveau/nouveau_drv.c |2 +- drivers/gpu/drm/radeon/radeon_drv.c |2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c |2 +- drivers/gpu/vga/vga_switcheroo.c | 87 - drivers/platform/x86/apple-gmux.c | 12 ++- include/drm/drmP.h|3 + include/linux/vga_switcheroo.h| 20 15 files changed, 302 insertions(+), 28 deletions(-)
[RFC PATCH 1/7] vga_switcheroo: Add support for switching only the DDC
During graphics driver initialization its useful to be able to mux only the DDC to the inactive client in order to read the EDID. Add a switch_ddc callback to allow capable handlers to provide this functionality, and add vga_switcheroo_switch_ddc() to allow DRM to mux only the DDC. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 39 +- include/linux/vga_switcheroo.h |4 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..ea6bcc2 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -252,6 +252,29 @@ 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 ret = 0; + int id; + + mutex_lock(vgasr_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler-switch_ddc) { + id = vgasr_priv.handler-get_client_id(pdev); + ret = vgasr_priv.handler-switch_ddc(id); + } + +out: + mutex_unlock(vgasr_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +365,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, event); } + if (vgasr_priv.handler-switch_ddc) { + ret = vgasr_priv.handler-switch_ddc(new_client-id); + if (ret) + return ret; + } + ret = vgasr_priv.handler-switchto(new_client-id); if (ret) - return ret; + goto restore_ddc; if (new_client-ops-reprobe) new_client-ops-reprobe(new_client-pdev); @@ -356,6 +385,14 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) new_client-active = true; return 0; + +restore_ddc: + if (vgasr_priv.handler-switch_ddc) { + int id = (new_client-id == VGA_SWITCHEROO_IGD) ? + VGA_SWITCHEROO_DIS : VGA_SWITCHEROO_IGD; + ret = vgasr_priv.handler-switch_ddc(id); + } + return ret; } static bool check_can_switch(void) diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index ddb419c..b0d0839 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -29,6 +29,7 @@ enum vga_switcheroo_client_id { }; struct vga_switcheroo_handler { + int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); int (*power_state)(enum vga_switcheroo_client_id id, enum vga_switcheroo_state state); @@ -53,6 +54,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_register_handler(struct vga_switcheroo_handler *handler); void vga_switcheroo_unregister_handler(void); @@ -66,6 +69,7 @@ static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} +static inline void vga_switcheroo_switch_ddc(struct pci_dev *pdev) { return NULL; } static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 2/7] vga_switcheroo: Add helper function to get the active client
Add vga_switcheroo_get_active_client() to allow drivers to get the active video client. This will be used by drivers wishing to temporarily mux only the DDC to the inactive client. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 14 ++ include/linux/vga_switcheroo.h |2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ea6bcc2..e53f67d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -205,6 +205,20 @@ find_active_client(struct list_head *head) return NULL; } +struct pci_dev *vga_switcheroo_get_active_client(void) +{ + struct vga_switcheroo_client *client; + struct pci_dev *pdev = NULL; + + mutex_lock(vgasr_mutex); + client = find_active_client(vgasr_priv.clients); + if (client) + pdev = client-pdev; + mutex_unlock(vgasr_mutex); + return pdev; +} +EXPORT_SYMBOL(vga_switcheroo_get_active_client); + int vga_switcheroo_get_client_state(struct pci_dev *pdev) { struct vga_switcheroo_client *client; diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index b0d0839..e361858 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -61,6 +61,7 @@ void vga_switcheroo_unregister_handler(void); 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); #else @@ -76,6 +77,7 @@ static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, int id, bool active) { return 0; } static inline void vga_switcheroo_unregister_handler(void) {} 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; } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 4/7] apple-gmux: Add switch_ddc support
The gmux allows muxing the DDC independently from the display, so support this functionality. This will allow reading the EDID for the inactive GPU, fixing issues with machines that either don't have a VBT or have invalid mode data in the VBT. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/platform/x86/apple-gmux.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index c72e81e..f702e90 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -269,14 +269,21 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switch_ddc(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + else + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + return 0; +} + static int gmux_switchto(enum vga_switcheroo_client_id id) { if (id == VGA_SWITCHEROO_IGD) { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); } else { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); } @@ -343,6 +350,7 @@ gmux_active_client(struct apple_gmux_data *gmux_data) } static struct vga_switcheroo_handler gmux_handler = { + .switch_ddc = gmux_switch_ddc, .switchto = gmux_switchto, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 5/7] drm/edid: Switch DDC when reading the EDID
Some dual graphics machines support muxing the DDC separately from the display, so make use of this functionality when reading the EDID on the inactive GPU. Also serialize drm_get_edid() with a mutex to avoid races on the DDC mux state. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/drm_edid.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a8743c3..1a4b661 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include linux/slab.h #include linux/i2c.h #include linux/module.h +#include linux/vga_switcheroo.h #include drmP.h #include drm_edid.h #include drm_edid_modes.h @@ -82,6 +83,8 @@ 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; @@ -395,12 +398,26 @@ 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); + } 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); + connector-display_info.raw_edid = (char *)edid; + mutex_unlock(drm_edid_mutex); return edid; } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 6/7] drm/pci: Add drm_put_pci_dev()
When deferred initialization support for pci devices is added some additional cleanup will be needed. Add a pci-specific put function to serve this purpose, and convert the pci drivers over to using it. For now it just calls drm_put_dev(), so this commit has no functional change. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/ast/ast_drv.c |2 +- drivers/gpu/drm/cirrus/cirrus_drv.c |2 +- drivers/gpu/drm/drm_pci.c |6 ++ drivers/gpu/drm/gma500/psb_drv.c |2 +- drivers/gpu/drm/i915/i915_drv.c |2 +- drivers/gpu/drm/mgag200/mgag200_drv.c |2 +- drivers/gpu/drm/nouveau/nouveau_drv.c |2 +- drivers/gpu/drm/radeon/radeon_drv.c |2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c |2 +- include/drm/drmP.h|1 + 10 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index d0c4574..001298d 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -72,7 +72,7 @@ ast_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 7053140..c7ca02b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -64,7 +64,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations cirrus_driver_fops = { diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 5320364..4896c96 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -388,6 +388,12 @@ err_g1: } EXPORT_SYMBOL(drm_get_pci_dev); +void drm_put_pci_dev(struct drm_device *dev) +{ + drm_put_dev(dev); +} +EXPORT_SYMBOL(drm_put_pci_dev); + /** * PCI device initialization. Called direct from modules at load time. * diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0c47374..d7c3c9c 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -585,7 +585,7 @@ static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) static void psb_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct dev_pm_ops psb_pm_ops = { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a24ffbe..86ae5a2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -856,7 +856,7 @@ i915_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int i915_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index ea1024d..a3b0a4a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -73,7 +73,7 @@ static void mga_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static const struct file_operations mgag200_driver_fops = { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 9a36f5f..b74b02a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -168,7 +168,7 @@ nouveau_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } int diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index d7269f4..298697a 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -308,7 +308,7 @@ radeon_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4d9edea..cf901cc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -982,7 +982,7 @@ static void vmw_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); + drm_put_pci_dev(dev); } static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index d6b67bb..eb99e96 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1748,6 +1748,7 @@ extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); extern
[RFC PATCH 7/7] drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready
On Mon, Aug 20, 2012 at 04:57:41PM +0100, Matthew Garrett wrote: > On Mon, Aug 20, 2012 at 10:56:33AM -0500, Seth Forshee wrote: > > On Mon, Aug 20, 2012 at 04:36:40PM +0100, Matthew Garrett wrote: > > > Won't this break the multiple cards with independent outputs case? > > > > Yes, if they don't have a switcheroo handler. I only have experience > > with one such machine, which had optimus graphics. My recollection is > > that it did have a switcheroo handler, which was only capable of > > controlling power to the discrete card. > > So if I have a desktop machine and install two graphics cards? Yeah, that would likely be broken. I'm not sure how we support both of these cases without doing something more like what I originally proposed, i.e. registering the LVDS connector even if it doesn't look like a panel is attached. I still honestly favor that approach, although it does come with its own set of challenges. The only other option I can come up with is to reprobe LVDS after switcheroo and add the connector at that time. I haven't investigated this option in detail, but at first glance it looks like there are at least some places where DRM isn't prepared to cope with adding connectors after initialization.
[RFC PATCH 7/7] drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready
On Mon, Aug 20, 2012 at 04:36:40PM +0100, Matthew Garrett wrote: > On Mon, Aug 20, 2012 at 10:31:04AM -0500, Seth Forshee wrote: > > + /* > > +* For secondary graphics devices shouldn't be initialized > > +* until the handler and primary graphics device have been > > +* registered with vga_switcheroo. > > +* > > +* FIXME: Is vga_default_device() reliable enough for this > > +* purpose? > > +* > > +* FIXME: If vga_switcheroo is disabled secondary devices > > +* never gets initialized. Is this okay? Maybe it is, since > > +* we can't switch to the secondary GPU anyway. > > +*/ > > Won't this break the multiple cards with independent outputs case? Yes, if they don't have a switcheroo handler. I only have experience with one such machine, which had optimus graphics. My recollection is that it did have a switcheroo handler, which was only capable of controlling power to the discrete card.
[RFC PATCH 7/7] drm/pci: Defer initialization of secondary graphics devices until switcheroo is ready
Deferring initiailzation of the secondary GPU until switcheroo is ready will allow successfully reading the EDID in systems which support muxing the DDC seperately from the display. Signed-off-by: Seth Forshee --- drivers/gpu/drm/drm_drv.c |3 + drivers/gpu/drm/drm_pci.c | 141 +++-- include/drm/drmP.h|2 + 3 files changed, 129 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9238de4..124fd8a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -276,6 +276,8 @@ static int __init drm_core_init(void) goto err_p3; } + drm_pci_module_init(); + DRM_INFO("Initialized %s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); return 0; @@ -291,6 +293,7 @@ err_p1: static void __exit drm_core_exit(void) { + drm_pci_module_exit(); remove_proc_entry("dri", NULL); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 4896c96..9da0cd2 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -40,6 +40,9 @@ #include #include #include +#include +#include +#include #include "drmP.h" /**/ @@ -297,19 +300,8 @@ static struct drm_bus drm_pci_bus = { .agp_init = drm_pci_agp_init, }; -/** - * Register. - * - * \param pdev - PCI device structure - * \param ent entry from the PCI ID table with device type flags - * \return zero on success or a negative number on failure. - * - * Attempt to gets inter module "drm" information. If we are first - * then register the character device and inter module information. - * Try and register, if we fail to register, backout previous work. - */ -int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, - struct drm_driver *driver) +int __drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, + struct drm_driver *driver) { struct drm_device *dev; int ret; @@ -334,8 +326,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, dev->hose = pdev->sysdata; #endif - mutex_lock(_global_mutex); - if ((ret = drm_fill_in_dev(dev, ent, driver))) { printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); goto err_g2; @@ -371,7 +361,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, driver->name, driver->major, driver->minor, driver->patchlevel, driver->date, pci_name(pdev), dev->primary->index); - mutex_unlock(_global_mutex); return 0; err_g4: @@ -386,10 +375,116 @@ err_g1: mutex_unlock(_global_mutex); return ret; } + +struct deferred_init_data { + struct list_head list; + struct pci_dev *pdev; + const struct pci_device_id *ent; + struct drm_driver *driver; +}; + +static LIST_HEAD(deferred_init_list); + +static void drm_deferred_init_work_fn(struct work_struct *work) +{ + struct deferred_init_data *di_data, *temp; + + mutex_lock(_global_mutex); + + if (!vga_switcheroo_handler_registered() || + !vga_switcheroo_get_active_client()) { + mutex_unlock(_global_mutex); + return; + } + + list_for_each_entry_safe(di_data, temp, _init_list, list) { + if (__drm_get_pci_dev(di_data->pdev, di_data->ent, + di_data->driver)) + DRM_ERROR("pci device initialization failed\n"); + list_del(_data->list); + kfree(di_data); + } + mutex_unlock(_global_mutex); +} +static DECLARE_WORK(deferred_init_work, drm_deferred_init_work_fn); + +static int drm_switcheroo_notifier_fn(struct notifier_block *nb, + unsigned long val, void *unused) +{ + if (val == VGA_SWITCHEROO_CLIENT_REGISTERED || + val == VGA_SWITCHEROO_HANDLER_REGISTERED) + queue_work(system_nrt_wq, _init_work); + return NOTIFY_OK; +} +static struct notifier_block drm_switcheroo_notifier = { + .notifier_call = drm_switcheroo_notifier_fn, +}; + +/** + * Register. + * + * \param pdev - PCI device structure + * \param ent entry from the PCI ID table with device type flags + * \return zero on success or a negative number on failure. + * + * Attempt to gets inter module "drm" information. If we are first + * then register the character device and inter module information. + * Try and register, if we fail to register, backout previous work. + */ +int drm_get_pci_dev(struct pci_dev *pdev, const struct pc
[RFC PATCH 5/7] drm/edid: Switch DDC when reading the EDID
Some dual graphics machines support muxing the DDC separately from the display, so make use of this functionality when reading the EDID on the inactive GPU. Also serialize drm_get_edid() with a mutex to avoid races on the DDC mux state. Signed-off-by: Seth Forshee --- drivers/gpu/drm/drm_edid.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a8743c3..1a4b661 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" @@ -82,6 +83,8 @@ 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; @@ -395,12 +398,26 @@ 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(_edid_mutex); + + if (pdev) { + active_pdev = vga_switcheroo_get_active_client(); + if (active_pdev != pdev) + vga_switcheroo_switch_ddc(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); + connector->display_info.raw_edid = (char *)edid; + mutex_unlock(_edid_mutex); return edid; } -- 1.7.9.5
[RFC PATCH 4/7] apple-gmux: Add switch_ddc support
The gmux allows muxing the DDC independently from the display, so support this functionality. This will allow reading the EDID for the inactive GPU, fixing issues with machines that either don't have a VBT or have invalid mode data in the VBT. Signed-off-by: Seth Forshee --- drivers/platform/x86/apple-gmux.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index c72e81e..f702e90 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -269,14 +269,21 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switch_ddc(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + else + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + return 0; +} + static int gmux_switchto(enum vga_switcheroo_client_id id) { if (id == VGA_SWITCHEROO_IGD) { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); } else { - gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); } @@ -343,6 +350,7 @@ gmux_active_client(struct apple_gmux_data *gmux_data) } static struct vga_switcheroo_handler gmux_handler = { + .switch_ddc = gmux_switch_ddc, .switchto = gmux_switchto, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, -- 1.7.9.5
[RFC PATCH 3/7] vga_switcheroo: Add notifier call chain for switcheroo events
DRM needs to be notified of client and handler registration in order to defer initialization of the secondary GPU until the EDID can be read from the LVDS panel. To support this add a notifier call chain to vga_switcheroo for subscribing to switcheroo events. Events are generated for registration and unregistration of handlers and clients. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 34 ++ include/linux/vga_switcheroo.h | 14 ++ 2 files changed, 48 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e53f67d..d5cd274 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,28 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; +static BLOCKING_NOTIFIER_HEAD(vga_switcheroo_notifier_list); + +int vga_switcheroo_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_register_notifier); + +int vga_switcheroo_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(_switcheroo_notifier_list, + nb); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_notifier); + +static int vga_switcheroo_notifier_call_chain(enum vga_switcheroo_event event) +{ + return blocking_notifier_call_chain(_switcheroo_notifier_list, + event, NULL); +} + static bool vga_switcheroo_ready(void) { /* we're ready if we get two clients + handler */ @@ -113,10 +136,18 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) vga_switcheroo_enable(); } mutex_unlock(_mutex); + + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_REGISTERED); return 0; } EXPORT_SYMBOL(vga_switcheroo_register_handler); +bool vga_switcheroo_handler_registered(void) +{ + return !!vgasr_priv.handler; +} +EXPORT_SYMBOL(vga_switcheroo_handler_registered); + void vga_switcheroo_unregister_handler(void) { mutex_lock(_mutex); @@ -127,6 +158,7 @@ void vga_switcheroo_unregister_handler(void) vgasr_priv.active = false; } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_HANDLER_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_handler); @@ -156,6 +188,7 @@ static int register_client(struct pci_dev *pdev, vga_switcheroo_enable(); } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_REGISTERED); return 0; } @@ -250,6 +283,7 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev) vgasr_priv.active = false; } mutex_unlock(_mutex); + vga_switcheroo_notifier_call_chain(VGA_SWITCHEROO_CLIENT_UNREGISTERED); } EXPORT_SYMBOL(vga_switcheroo_unregister_client); diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index e361858..c3d7c6f 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -11,6 +11,7 @@ #define _LINUX_VGA_SWITCHEROO_H_ #include +#include struct pci_dev; @@ -28,6 +29,13 @@ enum vga_switcheroo_client_id { VGA_SWITCHEROO_MAX_CLIENTS, }; +enum vga_switcheroo_event { + VGA_SWITCHEROO_CLIENT_REGISTERED, + VGA_SWITCHEROO_CLIENT_UNREGISTERED, + VGA_SWITCHEROO_HANDLER_REGISTERED, + VGA_SWITCHEROO_HANDLER_UNREGISTERED, +}; + struct vga_switcheroo_handler { int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); @@ -44,6 +52,9 @@ struct vga_switcheroo_client_ops { }; #if defined(CONFIG_VGA_SWITCHEROO) +int vga_switcheroo_register_notifier(struct notifier_block *nb); +int vga_switcheroo_unregister_notifier(struct notifier_block *nb); +bool vga_switcheroo_handler_registered(void); void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops); @@ -66,6 +77,9 @@ int vga_switcheroo_get_client_state(struct pci_dev *dev); #else +static inline int vga_switcheroo_register_notifier(struct notifier_block *nb) { return 0; } +static inline int vga_switcheroo_unregister_notifier(struct notifier_block *nb) { return 0; } +static inline bool vga_switcheroo_handler_registered(void) { return false; } static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0
[RFC PATCH 2/7] vga_switcheroo: Add helper function to get the active client
Add vga_switcheroo_get_active_client() to allow drivers to get the active video client. This will be used by drivers wishing to temporarily mux only the DDC to the inactive client. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 14 ++ include/linux/vga_switcheroo.h |2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ea6bcc2..e53f67d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -205,6 +205,20 @@ find_active_client(struct list_head *head) return NULL; } +struct pci_dev *vga_switcheroo_get_active_client(void) +{ + struct vga_switcheroo_client *client; + struct pci_dev *pdev = NULL; + + mutex_lock(_mutex); + client = find_active_client(_priv.clients); + if (client) + pdev = client->pdev; + mutex_unlock(_mutex); + return pdev; +} +EXPORT_SYMBOL(vga_switcheroo_get_active_client); + int vga_switcheroo_get_client_state(struct pci_dev *pdev) { struct vga_switcheroo_client *client; diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index b0d0839..e361858 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -61,6 +61,7 @@ void vga_switcheroo_unregister_handler(void); 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); #else @@ -76,6 +77,7 @@ static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, int id, bool active) { return 0; } static inline void vga_switcheroo_unregister_handler(void) {} 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; } -- 1.7.9.5
[RFC PATCH 1/7] vga_switcheroo: Add support for switching only the DDC
During graphics driver initialization its useful to be able to mux only the DDC to the inactive client in order to read the EDID. Add a switch_ddc callback to allow capable handlers to provide this functionality, and add vga_switcheroo_switch_ddc() to allow DRM to mux only the DDC. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 39 +- include/linux/vga_switcheroo.h |4 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..ea6bcc2 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -252,6 +252,29 @@ 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 ret = 0; + int id; + + mutex_lock(_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler->switch_ddc) { + id = vgasr_priv.handler->get_client_id(pdev); + ret = vgasr_priv.handler->switch_ddc(id); + } + +out: + mutex_unlock(_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +365,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, ); } + if (vgasr_priv.handler->switch_ddc) { + ret = vgasr_priv.handler->switch_ddc(new_client->id); + if (ret) + return ret; + } + ret = vgasr_priv.handler->switchto(new_client->id); if (ret) - return ret; + goto restore_ddc; if (new_client->ops->reprobe) new_client->ops->reprobe(new_client->pdev); @@ -356,6 +385,14 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) new_client->active = true; return 0; + +restore_ddc: + if (vgasr_priv.handler->switch_ddc) { + int id = (new_client->id == VGA_SWITCHEROO_IGD) ? + VGA_SWITCHEROO_DIS : VGA_SWITCHEROO_IGD; + ret = vgasr_priv.handler->switch_ddc(id); + } + return ret; } static bool check_can_switch(void) diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index ddb419c..b0d0839 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -29,6 +29,7 @@ enum vga_switcheroo_client_id { }; struct vga_switcheroo_handler { + int (*switch_ddc)(enum vga_switcheroo_client_id id); int (*switchto)(enum vga_switcheroo_client_id id); int (*power_state)(enum vga_switcheroo_client_id id, enum vga_switcheroo_state state); @@ -53,6 +54,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_register_handler(struct vga_switcheroo_handler *handler); void vga_switcheroo_unregister_handler(void); @@ -66,6 +69,7 @@ static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} +static inline void vga_switcheroo_switch_ddc(struct pci_dev *pdev) { return NULL; } static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, -- 1.7.9.5
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Fri, Aug 10, 2012 at 05:19:48PM -0500, Seth Forshee wrote: > First, I don't have a solution for the ordering of initialization. It > just happens to work out for me right now. Okay, I've got a proof-of-concept implementation of delaying secondary GPU initialization until the i2c can be muxed over to that card. It's not exactly pretty, but it is working. I'd really like to get some feedback on the concept and implementation before spending more time on it. Patches follow. One problem I'm aware of is if the switcheroo handler is in the driver for the secondary GPU. I think this was the case for a machine I used to have with Optimus graphics. In that scenario the secondary graphics device is never initialized because the switcheroo handler is registered during initialization of the secondary device. The driver load method would need to be split up to cope with this. Thanks, Seth
[PATCH v2 0/3] Display switching support for Apple laptops
This series adds display switching support for Apple laptops. The first two patches contain preparatory changes to vga_switcheroo, and the third contains the changes to support display switching with the gmux. While these patches will allow switching the display mux, most Macbook owners will not be able to switch GPUs in practice until the graphics drivers are updated to deal with missing or incorrect vbios information on Apple machines. These patches do fix an issue seen by some users of Linux on Macbooks, however. These users will switch to the internal GPU in OS X and then reboot into Linux to save power, but after S3 the gmux gets reset to the discrete GPU. Adding the display mux support will fix this problem by restoring the gmux state during resume. v2: Disable interrupts during suspend and re-enable them during resume to fix timeouts waiting for switching to complete after S3 Thanks, Seth Andreas Heider (1): apple-gmux: Add display mux support Seth Forshee (2): vga_switcheroo: Don't require handler init callback vga_switcheroo: Remove assumptions about registration/unregistration ordering drivers/gpu/drm/nouveau/nouveau_acpi.c |6 - drivers/gpu/vga/vga_switcheroo.c | 61 + drivers/platform/x86/apple-gmux.c | 224 3 files changed, 262 insertions(+), 29 deletions(-) ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 1/3] vga_switcheroo: Don't require handler init callback
This callback is a no-op in nouveau, and the upcoming apple-gmux switcheroo support won't require it either. Rather than forcing drivers to stub it out, just make it optional and remove the callback from nouveau. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/nouveau/nouveau_acpi.c |6 -- drivers/gpu/vga/vga_switcheroo.c |3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index fc841e8..26ebffe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); } -static int nouveau_dsm_init(void) -{ - return 0; -} - static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { /* easy option one - intel vendor ID means Integrated */ @@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev) static struct vga_switcheroo_handler nouveau_dsm_handler = { .switchto = nouveau_dsm_switchto, .power_state = nouveau_dsm_power_state, - .init = nouveau_dsm_init, .get_client_id = nouveau_dsm_get_client_id, }; diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 5b3c7d1..ec9069d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -98,7 +98,8 @@ static void vga_switcheroo_enable(void) struct vga_switcheroo_client *client; /* call the handler to init */ - vgasr_priv.handler-init(); + if (vgasr_priv.handler-init) + vgasr_priv.handler-init(); list_for_each_entry(client, vgasr_priv.clients, list) { if (client-id != -1) -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 2/3] vga_switcheroo: Remove assumptions about registration/unregistration ordering
vga_switcheroo assumes that the handler will be registered before the last client, otherwise switching will not be enabled. Likewise it's assumed that the handler will not be unregistered without at least one client also being unregistered, otherwise switching will remain enabled despite no longer having a handler. These assumptions cannot be enforced if the handler is in a separate driver from both clients, as with the gmux found in Apple laptops. Remove this assumption. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 58 +++--- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ec9069d..e25cf31 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; -int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) -{ - mutex_lock(vgasr_mutex); - if (vgasr_priv.handler) { - mutex_unlock(vgasr_mutex); - return -EINVAL; - } - - vgasr_priv.handler = handler; - mutex_unlock(vgasr_mutex); - return 0; -} -EXPORT_SYMBOL(vga_switcheroo_register_handler); - -void vga_switcheroo_unregister_handler(void) +static bool vga_switcheroo_ready(void) { - mutex_lock(vgasr_mutex); - vgasr_priv.handler = NULL; - mutex_unlock(vgasr_mutex); + /* we're ready if we get two clients + handler */ + return !vgasr_priv.active + vgasr_priv.registered_clients == 2 vgasr_priv.handler; } -EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static void vga_switcheroo_enable(void) { @@ -114,6 +99,37 @@ static void vga_switcheroo_enable(void) vgasr_priv.active = true; } +int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) +{ + mutex_lock(vgasr_mutex); + if (vgasr_priv.handler) { + mutex_unlock(vgasr_mutex); + return -EINVAL; + } + + vgasr_priv.handler = handler; + if (vga_switcheroo_ready()) { + printk(KERN_INFO vga_switcheroo: enabled\n); + vga_switcheroo_enable(); + } + mutex_unlock(vgasr_mutex); + return 0; +} +EXPORT_SYMBOL(vga_switcheroo_register_handler); + +void vga_switcheroo_unregister_handler(void) +{ + mutex_lock(vgasr_mutex); + vgasr_priv.handler = NULL; + if (vgasr_priv.active) { + pr_info(vga_switcheroo: disabled\n); + vga_switcheroo_debugfs_fini(vgasr_priv); + vgasr_priv.active = false; + } + mutex_unlock(vgasr_mutex); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_handler); + static int register_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active) @@ -135,9 +151,7 @@ static int register_client(struct pci_dev *pdev, if (client_is_vga(client)) vgasr_priv.registered_clients++; - /* if we get two clients + handler */ - if (!vgasr_priv.active - vgasr_priv.registered_clients == 2 vgasr_priv.handler) { + if (vga_switcheroo_ready()) { printk(KERN_INFO vga_switcheroo: enabled\n); vga_switcheroo_enable(); } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 3/3] apple-gmux: Add display mux support
From: Andreas Heider andr...@meetr.de Add support for the gmux display muxing functionality and register a mux handler with vga_switcheroo. Signed-off-by: Andreas Heider andr...@meetr.de Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/platform/x86/apple-gmux.c | 224 + 1 file changed, 224 insertions(+) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 612b6f6..c72e81e 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -2,6 +2,7 @@ * Gmux driver for Apple laptops * * Copyright (C) Canonical Ltd. seth.fors...@canonical.com + * Copyright (C) 2010-2012 Andreas Heider andr...@meetr.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,6 +20,8 @@ #include linux/apple_bl.h #include linux/slab.h #include linux/delay.h +#include linux/pci.h +#include linux/vga_switcheroo.h #include acpi/video.h #include asm/io.h @@ -29,8 +32,17 @@ struct apple_gmux_data { struct mutex index_lock; struct backlight_device *bdev; + + /* switcheroo data */ + acpi_handle dhandle; + int gpe; + enum vga_switcheroo_client_id resume_client_id; + enum vga_switcheroo_state power_state; + struct completion powerchange_done; }; +static struct apple_gmux_data *apple_gmux_data; + /* * gmux port offsets. Many of these are not yet used, but may be in the * future, and it's useful to have them documented here anyhow. @@ -257,6 +269,146 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); + } else { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); + } + + return 0; +} + +static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, + enum vga_switcheroo_state state) +{ + INIT_COMPLETION(gmux_data-powerchange_done); + + if (state == VGA_SWITCHEROO_ON) { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + pr_debug(Discrete card powered up\n); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + pr_debug(Discrete card powered down\n); + } + + gmux_data-power_state = state; + + if (gmux_data-gpe = 0 + !wait_for_completion_interruptible_timeout(gmux_data-powerchange_done, + msecs_to_jiffies(200))) + pr_warn(Timeout waiting for gmux switch to complete\n); + + return 0; +} + +static int gmux_set_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + return gmux_set_discrete_state(apple_gmux_data, state); +} + +static int gmux_get_client_id(struct pci_dev *pdev) +{ + /* +* Early Macbook Pros with switchable graphics use nvidia +* integrated graphics. Hardcode that the 9400M is integrated. +*/ + if (pdev-vendor == PCI_VENDOR_ID_INTEL) + return VGA_SWITCHEROO_IGD; + else if (pdev-vendor == PCI_VENDOR_ID_NVIDIA +pdev-device == 0x0863) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static enum vga_switcheroo_client_id +gmux_active_client(struct apple_gmux_data *gmux_data) +{ + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler gmux_handler = { + .switchto = gmux_switchto, + .power_state = gmux_set_power_state, + .get_client_id = gmux_get_client_id, +}; + +static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_DISABLE); +} + +static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_ENABLE); +} + +static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data
[PATCH v2 3/3] apple-gmux: Add display mux support
From: Andreas Heider <andr...@meetr.de> Add support for the gmux display muxing functionality and register a mux handler with vga_switcheroo. Signed-off-by: Andreas Heider Signed-off-by: Seth Forshee --- drivers/platform/x86/apple-gmux.c | 224 + 1 file changed, 224 insertions(+) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 612b6f6..c72e81e 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -2,6 +2,7 @@ * Gmux driver for Apple laptops * * Copyright (C) Canonical Ltd. + * Copyright (C) 2010-2012 Andreas Heider * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -29,8 +32,17 @@ struct apple_gmux_data { struct mutex index_lock; struct backlight_device *bdev; + + /* switcheroo data */ + acpi_handle dhandle; + int gpe; + enum vga_switcheroo_client_id resume_client_id; + enum vga_switcheroo_state power_state; + struct completion powerchange_done; }; +static struct apple_gmux_data *apple_gmux_data; + /* * gmux port offsets. Many of these are not yet used, but may be in the * future, and it's useful to have them documented here anyhow. @@ -257,6 +269,146 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); + } else { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); + } + + return 0; +} + +static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, + enum vga_switcheroo_state state) +{ + INIT_COMPLETION(gmux_data->powerchange_done); + + if (state == VGA_SWITCHEROO_ON) { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + pr_debug("Discrete card powered up\n"); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + pr_debug("Discrete card powered down\n"); + } + + gmux_data->power_state = state; + + if (gmux_data->gpe >= 0 && + !wait_for_completion_interruptible_timeout(_data->powerchange_done, + msecs_to_jiffies(200))) + pr_warn("Timeout waiting for gmux switch to complete\n"); + + return 0; +} + +static int gmux_set_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + return gmux_set_discrete_state(apple_gmux_data, state); +} + +static int gmux_get_client_id(struct pci_dev *pdev) +{ + /* +* Early Macbook Pros with switchable graphics use nvidia +* integrated graphics. Hardcode that the 9400M is integrated. +*/ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + return VGA_SWITCHEROO_IGD; + else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && +pdev->device == 0x0863) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static enum vga_switcheroo_client_id +gmux_active_client(struct apple_gmux_data *gmux_data) +{ + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler gmux_handler = { + .switchto = gmux_switchto, + .power_state = gmux_set_power_state, + .get_client_id = gmux_get_client_id, +}; + +static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_DISABLE); +} + +static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_ENABLE); +} + +static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data) +{ + return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS); +} + +static void g
[PATCH v2 2/3] vga_switcheroo: Remove assumptions about registration/unregistration ordering
vga_switcheroo assumes that the handler will be registered before the last client, otherwise switching will not be enabled. Likewise it's assumed that the handler will not be unregistered without at least one client also being unregistered, otherwise switching will remain enabled despite no longer having a handler. These assumptions cannot be enforced if the handler is in a separate driver from both clients, as with the gmux found in Apple laptops. Remove this assumption. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 58 +++--- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ec9069d..e25cf31 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; -int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) -{ - mutex_lock(_mutex); - if (vgasr_priv.handler) { - mutex_unlock(_mutex); - return -EINVAL; - } - - vgasr_priv.handler = handler; - mutex_unlock(_mutex); - return 0; -} -EXPORT_SYMBOL(vga_switcheroo_register_handler); - -void vga_switcheroo_unregister_handler(void) +static bool vga_switcheroo_ready(void) { - mutex_lock(_mutex); - vgasr_priv.handler = NULL; - mutex_unlock(_mutex); + /* we're ready if we get two clients + handler */ + return !vgasr_priv.active && + vgasr_priv.registered_clients == 2 && vgasr_priv.handler; } -EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static void vga_switcheroo_enable(void) { @@ -114,6 +99,37 @@ static void vga_switcheroo_enable(void) vgasr_priv.active = true; } +int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) +{ + mutex_lock(_mutex); + if (vgasr_priv.handler) { + mutex_unlock(_mutex); + return -EINVAL; + } + + vgasr_priv.handler = handler; + if (vga_switcheroo_ready()) { + printk(KERN_INFO "vga_switcheroo: enabled\n"); + vga_switcheroo_enable(); + } + mutex_unlock(_mutex); + return 0; +} +EXPORT_SYMBOL(vga_switcheroo_register_handler); + +void vga_switcheroo_unregister_handler(void) +{ + mutex_lock(_mutex); + vgasr_priv.handler = NULL; + if (vgasr_priv.active) { + pr_info("vga_switcheroo: disabled\n"); + vga_switcheroo_debugfs_fini(_priv); + vgasr_priv.active = false; + } + mutex_unlock(_mutex); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_handler); + static int register_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active) @@ -135,9 +151,7 @@ static int register_client(struct pci_dev *pdev, if (client_is_vga(client)) vgasr_priv.registered_clients++; - /* if we get two clients + handler */ - if (!vgasr_priv.active && - vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { + if (vga_switcheroo_ready()) { printk(KERN_INFO "vga_switcheroo: enabled\n"); vga_switcheroo_enable(); } -- 1.7.9.5
[PATCH v2 1/3] vga_switcheroo: Don't require handler init callback
This callback is a no-op in nouveau, and the upcoming apple-gmux switcheroo support won't require it either. Rather than forcing drivers to stub it out, just make it optional and remove the callback from nouveau. Signed-off-by: Seth Forshee --- drivers/gpu/drm/nouveau/nouveau_acpi.c |6 -- drivers/gpu/vga/vga_switcheroo.c |3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index fc841e8..26ebffe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); } -static int nouveau_dsm_init(void) -{ - return 0; -} - static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { /* easy option one - intel vendor ID means Integrated */ @@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev) static struct vga_switcheroo_handler nouveau_dsm_handler = { .switchto = nouveau_dsm_switchto, .power_state = nouveau_dsm_power_state, - .init = nouveau_dsm_init, .get_client_id = nouveau_dsm_get_client_id, }; diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 5b3c7d1..ec9069d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -98,7 +98,8 @@ static void vga_switcheroo_enable(void) struct vga_switcheroo_client *client; /* call the handler to init */ - vgasr_priv.handler->init(); + if (vgasr_priv.handler->init) + vgasr_priv.handler->init(); list_for_each_entry(client, _priv.clients, list) { if (client->id != -1) -- 1.7.9.5
[PATCH v2 0/3] Display switching support for Apple laptops
This series adds display switching support for Apple laptops. The first two patches contain preparatory changes to vga_switcheroo, and the third contains the changes to support display switching with the gmux. While these patches will allow switching the display mux, most Macbook owners will not be able to switch GPUs in practice until the graphics drivers are updated to deal with missing or incorrect vbios information on Apple machines. These patches do fix an issue seen by some users of Linux on Macbooks, however. These users will switch to the internal GPU in OS X and then reboot into Linux to save power, but after S3 the gmux gets reset to the discrete GPU. Adding the display mux support will fix this problem by restoring the gmux state during resume. v2: Disable interrupts during suspend and re-enable them during resume to fix timeouts waiting for switching to complete after S3 Thanks, Seth Andreas Heider (1): apple-gmux: Add display mux support Seth Forshee (2): vga_switcheroo: Don't require handler init callback vga_switcheroo: Remove assumptions about registration/unregistration ordering drivers/gpu/drm/nouveau/nouveau_acpi.c |6 - drivers/gpu/vga/vga_switcheroo.c | 61 + drivers/platform/x86/apple-gmux.c | 224 3 files changed, 262 insertions(+), 29 deletions(-)
[PATCH 0/3] Display switching support for Apple laptops
This series adds display switching support for Apple laptops. The first two patches contain preparatory changes to vga_switcheroo, and the third contains the changes to support display switching with the gmux. While these patches will allow switching the display mux, most Macbook owners will not be able to switch GPUs in practice until the graphics drivers are updated to deal with missing or incorrect vbios information on Apple machines. These patches do fix an issue seen by some users of Linux on Macbooks, however. These users will switch to the internal GPU in OS X and then reboot into Linux to save power, but after S3 the gmux gets reset to the discrete GPU. Adding the display mux support will fix this problem by restoring the gmux state during resume. Thanks, Seth Andreas Heider (1): apple-gmux: Add display mux support Seth Forshee (2): vga_switcheroo: Don't require handler init callback vga_switcheroo: Remove assumptions about registration/unregistration ordering drivers/gpu/drm/nouveau/nouveau_acpi.c |6 - drivers/gpu/vga/vga_switcheroo.c | 61 + drivers/platform/x86/apple-gmux.c | 222 3 files changed, 260 insertions(+), 29 deletions(-) ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/3] vga_switcheroo: Don't require handler init callback
This callback is a no-op in nouveau, and the upcoming apple-gmux switcheroo support won't require it either. Rather than forcing drivers to stub it out, just make it optional and remove the callback from nouveau. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/nouveau/nouveau_acpi.c |6 -- drivers/gpu/vga/vga_switcheroo.c |3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index fc841e8..26ebffe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); } -static int nouveau_dsm_init(void) -{ - return 0; -} - static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { /* easy option one - intel vendor ID means Integrated */ @@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev) static struct vga_switcheroo_handler nouveau_dsm_handler = { .switchto = nouveau_dsm_switchto, .power_state = nouveau_dsm_power_state, - .init = nouveau_dsm_init, .get_client_id = nouveau_dsm_get_client_id, }; diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 5b3c7d1..ec9069d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -98,7 +98,8 @@ static void vga_switcheroo_enable(void) struct vga_switcheroo_client *client; /* call the handler to init */ - vgasr_priv.handler-init(); + if (vgasr_priv.handler-init) + vgasr_priv.handler-init(); list_for_each_entry(client, vgasr_priv.clients, list) { if (client-id != -1) -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/3] vga_switcheroo: Remove assumptions about registration/unregistration ordering
vga_switcheroo assumes that the handler will be registered before the last client, otherwise switching will not be enabled. Likewise it's assumed that the handler will not be unregistered without at least one client also being unregistered, otherwise switching will remain enabled despite no longer having a handler. These assumptions cannot be enforced if the handler is in a separate driver from both clients, as with the gmux found in Apple laptops. Remove this assumption. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/vga/vga_switcheroo.c | 58 +++--- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ec9069d..e25cf31 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; -int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) -{ - mutex_lock(vgasr_mutex); - if (vgasr_priv.handler) { - mutex_unlock(vgasr_mutex); - return -EINVAL; - } - - vgasr_priv.handler = handler; - mutex_unlock(vgasr_mutex); - return 0; -} -EXPORT_SYMBOL(vga_switcheroo_register_handler); - -void vga_switcheroo_unregister_handler(void) +static bool vga_switcheroo_ready(void) { - mutex_lock(vgasr_mutex); - vgasr_priv.handler = NULL; - mutex_unlock(vgasr_mutex); + /* we're ready if we get two clients + handler */ + return !vgasr_priv.active + vgasr_priv.registered_clients == 2 vgasr_priv.handler; } -EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static void vga_switcheroo_enable(void) { @@ -114,6 +99,37 @@ static void vga_switcheroo_enable(void) vgasr_priv.active = true; } +int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) +{ + mutex_lock(vgasr_mutex); + if (vgasr_priv.handler) { + mutex_unlock(vgasr_mutex); + return -EINVAL; + } + + vgasr_priv.handler = handler; + if (vga_switcheroo_ready()) { + printk(KERN_INFO vga_switcheroo: enabled\n); + vga_switcheroo_enable(); + } + mutex_unlock(vgasr_mutex); + return 0; +} +EXPORT_SYMBOL(vga_switcheroo_register_handler); + +void vga_switcheroo_unregister_handler(void) +{ + mutex_lock(vgasr_mutex); + vgasr_priv.handler = NULL; + if (vgasr_priv.active) { + pr_info(vga_switcheroo: disabled\n); + vga_switcheroo_debugfs_fini(vgasr_priv); + vgasr_priv.active = false; + } + mutex_unlock(vgasr_mutex); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_handler); + static int register_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active) @@ -135,9 +151,7 @@ static int register_client(struct pci_dev *pdev, if (client_is_vga(client)) vgasr_priv.registered_clients++; - /* if we get two clients + handler */ - if (!vgasr_priv.active - vgasr_priv.registered_clients == 2 vgasr_priv.handler) { + if (vga_switcheroo_ready()) { printk(KERN_INFO vga_switcheroo: enabled\n); vga_switcheroo_enable(); } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 3/3] apple-gmux: Add display mux support
From: Andreas Heider andr...@meetr.de Add support for the gmux display muxing functionality and register a mux handler with vga_switcheroo. Signed-off-by: Andreas Heider andr...@meetr.de Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/platform/x86/apple-gmux.c | 222 + 1 file changed, 222 insertions(+) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 612b6f6..6dc9e80 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -2,6 +2,7 @@ * Gmux driver for Apple laptops * * Copyright (C) Canonical Ltd. seth.fors...@canonical.com + * Copyright (C) 2010-2012 Andreas Heider andr...@meetr.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,6 +20,8 @@ #include linux/apple_bl.h #include linux/slab.h #include linux/delay.h +#include linux/pci.h +#include linux/vga_switcheroo.h #include acpi/video.h #include asm/io.h @@ -29,8 +32,17 @@ struct apple_gmux_data { struct mutex index_lock; struct backlight_device *bdev; + + /* switcheroo data */ + acpi_handle dhandle; + int gpe; + enum vga_switcheroo_client_id resume_client_id; + enum vga_switcheroo_state power_state; + struct completion powerchange_done; }; +static struct apple_gmux_data *apple_gmux_data; + /* * gmux port offsets. Many of these are not yet used, but may be in the * future, and it's useful to have them documented here anyhow. @@ -257,6 +269,144 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); + } else { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); + } + + return 0; +} + +static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, + enum vga_switcheroo_state state) +{ + INIT_COMPLETION(gmux_data-powerchange_done); + + if (state == VGA_SWITCHEROO_ON) { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + pr_debug(Discrete card powered up\n); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + pr_debug(Discrete card powered down\n); + } + + gmux_data-power_state = state; + + if (gmux_data-gpe = 0 + !wait_for_completion_interruptible_timeout(gmux_data-powerchange_done, + msecs_to_jiffies(200))) + pr_warn(Timeout waiting for gmux switch to complete\n); + + return 0; +} + +static int gmux_set_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + return gmux_set_discrete_state(apple_gmux_data, state); +} + +static int gmux_get_client_id(struct pci_dev *pdev) +{ + /* +* Early Macbook Pros with switchable graphics use nvidia +* integrated graphics. Hardcode that the 9400M is integrated. +*/ + if (pdev-vendor == PCI_VENDOR_ID_INTEL) + return VGA_SWITCHEROO_IGD; + else if (pdev-vendor == PCI_VENDOR_ID_NVIDIA +pdev-device == 0x0863) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static enum vga_switcheroo_client_id +gmux_active_client(struct apple_gmux_data *gmux_data) +{ + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler gmux_handler = { + .switchto = gmux_switchto, + .power_state = gmux_set_power_state, + .get_client_id = gmux_get_client_id, +}; + +static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_DISABLE); +} + +static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_ENABLE); +} + +static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data
[PATCH 3/3] apple-gmux: Add display mux support
From: Andreas Heider <andr...@meetr.de> Add support for the gmux display muxing functionality and register a mux handler with vga_switcheroo. Signed-off-by: Andreas Heider Signed-off-by: Seth Forshee --- drivers/platform/x86/apple-gmux.c | 222 + 1 file changed, 222 insertions(+) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 612b6f6..6dc9e80 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -2,6 +2,7 @@ * Gmux driver for Apple laptops * * Copyright (C) Canonical Ltd. + * Copyright (C) 2010-2012 Andreas Heider * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -29,8 +32,17 @@ struct apple_gmux_data { struct mutex index_lock; struct backlight_device *bdev; + + /* switcheroo data */ + acpi_handle dhandle; + int gpe; + enum vga_switcheroo_client_id resume_client_id; + enum vga_switcheroo_state power_state; + struct completion powerchange_done; }; +static struct apple_gmux_data *apple_gmux_data; + /* * gmux port offsets. Many of these are not yet used, but may be in the * future, and it's useful to have them documented here anyhow. @@ -257,6 +269,144 @@ static const struct backlight_ops gmux_bl_ops = { .update_status = gmux_update_status, }; +static int gmux_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); + } else { + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); + gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); + } + + return 0; +} + +static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, + enum vga_switcheroo_state state) +{ + INIT_COMPLETION(gmux_data->powerchange_done); + + if (state == VGA_SWITCHEROO_ON) { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + pr_debug("Discrete card powered up\n"); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + pr_debug("Discrete card powered down\n"); + } + + gmux_data->power_state = state; + + if (gmux_data->gpe >= 0 && + !wait_for_completion_interruptible_timeout(_data->powerchange_done, + msecs_to_jiffies(200))) + pr_warn("Timeout waiting for gmux switch to complete\n"); + + return 0; +} + +static int gmux_set_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + return gmux_set_discrete_state(apple_gmux_data, state); +} + +static int gmux_get_client_id(struct pci_dev *pdev) +{ + /* +* Early Macbook Pros with switchable graphics use nvidia +* integrated graphics. Hardcode that the 9400M is integrated. +*/ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + return VGA_SWITCHEROO_IGD; + else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && +pdev->device == 0x0863) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static enum vga_switcheroo_client_id +gmux_active_client(struct apple_gmux_data *gmux_data) +{ + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler gmux_handler = { + .switchto = gmux_switchto, + .power_state = gmux_set_power_state, + .get_client_id = gmux_get_client_id, +}; + +static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_DISABLE); +} + +static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) +{ + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, + GMUX_INTERRUPT_ENABLE); +} + +static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data) +{ + return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS); +} + +static void g
[PATCH 2/3] vga_switcheroo: Remove assumptions about registration/unregistration ordering
vga_switcheroo assumes that the handler will be registered before the last client, otherwise switching will not be enabled. Likewise it's assumed that the handler will not be unregistered without at least one client also being unregistered, otherwise switching will remain enabled despite no longer having a handler. These assumptions cannot be enforced if the handler is in a separate driver from both clients, as with the gmux found in Apple laptops. Remove this assumption. Signed-off-by: Seth Forshee --- drivers/gpu/vga/vga_switcheroo.c | 58 +++--- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index ec9069d..e25cf31 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; -int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) -{ - mutex_lock(_mutex); - if (vgasr_priv.handler) { - mutex_unlock(_mutex); - return -EINVAL; - } - - vgasr_priv.handler = handler; - mutex_unlock(_mutex); - return 0; -} -EXPORT_SYMBOL(vga_switcheroo_register_handler); - -void vga_switcheroo_unregister_handler(void) +static bool vga_switcheroo_ready(void) { - mutex_lock(_mutex); - vgasr_priv.handler = NULL; - mutex_unlock(_mutex); + /* we're ready if we get two clients + handler */ + return !vgasr_priv.active && + vgasr_priv.registered_clients == 2 && vgasr_priv.handler; } -EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static void vga_switcheroo_enable(void) { @@ -114,6 +99,37 @@ static void vga_switcheroo_enable(void) vgasr_priv.active = true; } +int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) +{ + mutex_lock(_mutex); + if (vgasr_priv.handler) { + mutex_unlock(_mutex); + return -EINVAL; + } + + vgasr_priv.handler = handler; + if (vga_switcheroo_ready()) { + printk(KERN_INFO "vga_switcheroo: enabled\n"); + vga_switcheroo_enable(); + } + mutex_unlock(_mutex); + return 0; +} +EXPORT_SYMBOL(vga_switcheroo_register_handler); + +void vga_switcheroo_unregister_handler(void) +{ + mutex_lock(_mutex); + vgasr_priv.handler = NULL; + if (vgasr_priv.active) { + pr_info("vga_switcheroo: disabled\n"); + vga_switcheroo_debugfs_fini(_priv); + vgasr_priv.active = false; + } + mutex_unlock(_mutex); +} +EXPORT_SYMBOL(vga_switcheroo_unregister_handler); + static int register_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active) @@ -135,9 +151,7 @@ static int register_client(struct pci_dev *pdev, if (client_is_vga(client)) vgasr_priv.registered_clients++; - /* if we get two clients + handler */ - if (!vgasr_priv.active && - vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { + if (vga_switcheroo_ready()) { printk(KERN_INFO "vga_switcheroo: enabled\n"); vga_switcheroo_enable(); } -- 1.7.9.5
[PATCH 1/3] vga_switcheroo: Don't require handler init callback
This callback is a no-op in nouveau, and the upcoming apple-gmux switcheroo support won't require it either. Rather than forcing drivers to stub it out, just make it optional and remove the callback from nouveau. Signed-off-by: Seth Forshee --- drivers/gpu/drm/nouveau/nouveau_acpi.c |6 -- drivers/gpu/vga/vga_switcheroo.c |3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index fc841e8..26ebffe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); } -static int nouveau_dsm_init(void) -{ - return 0; -} - static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { /* easy option one - intel vendor ID means Integrated */ @@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev) static struct vga_switcheroo_handler nouveau_dsm_handler = { .switchto = nouveau_dsm_switchto, .power_state = nouveau_dsm_power_state, - .init = nouveau_dsm_init, .get_client_id = nouveau_dsm_get_client_id, }; diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 5b3c7d1..ec9069d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -98,7 +98,8 @@ static void vga_switcheroo_enable(void) struct vga_switcheroo_client *client; /* call the handler to init */ - vgasr_priv.handler->init(); + if (vgasr_priv.handler->init) + vgasr_priv.handler->init(); list_for_each_entry(client, _priv.clients, list) { if (client->id != -1) -- 1.7.9.5
[PATCH 0/3] Display switching support for Apple laptops
This series adds display switching support for Apple laptops. The first two patches contain preparatory changes to vga_switcheroo, and the third contains the changes to support display switching with the gmux. While these patches will allow switching the display mux, most Macbook owners will not be able to switch GPUs in practice until the graphics drivers are updated to deal with missing or incorrect vbios information on Apple machines. These patches do fix an issue seen by some users of Linux on Macbooks, however. These users will switch to the internal GPU in OS X and then reboot into Linux to save power, but after S3 the gmux gets reset to the discrete GPU. Adding the display mux support will fix this problem by restoring the gmux state during resume. Thanks, Seth Andreas Heider (1): apple-gmux: Add display mux support Seth Forshee (2): vga_switcheroo: Don't require handler init callback vga_switcheroo: Remove assumptions about registration/unregistration ordering drivers/gpu/drm/nouveau/nouveau_acpi.c |6 - drivers/gpu/vga/vga_switcheroo.c | 61 + drivers/platform/x86/apple-gmux.c | 222 3 files changed, 260 insertions(+), 29 deletions(-)
Re: [RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Mon, Aug 06, 2012 at 07:44:16AM +1000, Dave Airlie wrote: The correct approach is clearly to just have the drm core change the i2c mux before requesting edid, but that's made difficult because of the absence of ordering guarantees in initialisation. I don't like quirking this, since we're then back to the situation of potentially having to add every new piece of related hardware to the quirk list. The correct approach of switching the mux before we fetch the edid is actualy the one I fear will result in fragile code: Only run on few machines, and as you say with tons of funky interactions with the init sequence ordering. And I guess people will bitchmoan about the flickering this will cause ;-) As long as it's only apple shipping multi-gpu machines with broken/non-existing vbt, I'll happily stomach the quirk list entries. They're bad, but imo the lesser evil. Well in theory you can switch the ddc lines without switching the other lines, so we could do a mutex protected mux switch around edid retrival, Of course someone would have to code it up first then we could see how ugly it would be. I coded it up, and it's not really too bad. I've put a dump of my local changes below. But there are a couple of problems. First, I don't have a solution for the ordering of initialization. It just happens to work out for me right now. Even so, the code only works if I delay loading i915. apple-gmux is definitely initializing first so the i2c mux should be getting switched, but the transactions are failing. [ 19.445658] [drm:gmbus_xfer], GMBUS [i915 gmbus panel] timed out after NAK [ 19.445672] [drm:gmbus_xfer], GMBUS [i915 gmbus panel] NAK for addr: 0050 r(1) But if I prevent i915 from being auto-loaded and load later on it works fine. I haven't been able to figure out what's going on. Any ideas? Thanks, Seth diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a8743c3..3f18e8a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include linux/slab.h #include linux/i2c.h #include linux/module.h +#include linux/vga_switcheroo.h #include drmP.h #include drm_edid.h #include drm_edid_modes.h @@ -82,6 +83,8 @@ 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; @@ -395,12 +398,25 @@ 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(); + vga_switcheroo_switch_ddc(pdev); + } if (drm_probe_ddc(adapter)) edid = (struct edid *)drm_do_get_edid(connector, adapter); + if (active_pdev) + vga_switcheroo_switch_ddc(active_pdev); + connector-display_info.raw_edid = (char *)edid; + mutex_unlock(drm_edid_mutex); return edid; } diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..e53f67d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -205,6 +205,20 @@ find_active_client(struct list_head *head) return NULL; } +struct pci_dev *vga_switcheroo_get_active_client(void) +{ + struct vga_switcheroo_client *client; + struct pci_dev *pdev = NULL; + + mutex_lock(vgasr_mutex); + client = find_active_client(vgasr_priv.clients); + if (client) + pdev = client-pdev; + mutex_unlock(vgasr_mutex); + return pdev; +} +EXPORT_SYMBOL(vga_switcheroo_get_active_client); + int vga_switcheroo_get_client_state(struct pci_dev *pdev) { struct vga_switcheroo_client *client; @@ -252,6 +266,29 @@ 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 ret = 0; + int id; + + mutex_lock(vgasr_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler-switch_ddc) { + id = vgasr_priv.handler-get_client_id(pdev); + ret = vgasr_priv.handler-switch_ddc(id); + } + +out: + mutex_unlock(vgasr_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +379,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, event); } + if (vgasr_priv.handler-switch_ddc) { +
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Mon, Aug 06, 2012 at 07:44:16AM +1000, Dave Airlie wrote: > >> The "correct" approach is clearly to just have the drm core change the > >> i2c mux before requesting edid, but that's made difficult because of the > >> absence of ordering guarantees in initialisation. I don't like quirking > >> this, since we're then back to the situation of potentially having to > >> add every new piece of related hardware to the quirk list. > > > > The "correct" approach of switching the mux before we fetch the edid is > > actualy the one I fear will result in fragile code: Only run on few > > machines, and as you say with tons of funky interactions with the init > > sequence ordering. And I guess people will bitch about the flickering > > this will cause ;-) > > > > As long as it's only apple shipping multi-gpu machines with > > broken/non-existing vbt, I'll happily stomach the quirk list entries. > > They're bad, but imo the lesser evil. > > Well in theory you can switch the ddc lines without switching the other lines, > so we could do a mutex protected mux switch around edid retrival, > > Of course someone would have to code it up first then we could see how > ugly it would be. I coded it up, and it's not really too bad. I've put a dump of my local changes below. But there are a couple of problems. First, I don't have a solution for the ordering of initialization. It just happens to work out for me right now. Even so, the code only works if I delay loading i915. apple-gmux is definitely initializing first so the i2c mux should be getting switched, but the transactions are failing. [ 19.445658] [drm:gmbus_xfer], GMBUS [i915 gmbus panel] timed out after NAK [ 19.445672] [drm:gmbus_xfer], GMBUS [i915 gmbus panel] NAK for addr: 0050 r(1) But if I prevent i915 from being auto-loaded and load later on it works fine. I haven't been able to figure out what's going on. Any ideas? Thanks, Seth diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a8743c3..3f18e8a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" @@ -82,6 +83,8 @@ 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; @@ -395,12 +398,25 @@ 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(_edid_mutex); + + if (pdev) { + active_pdev = vga_switcheroo_get_active_client(); + vga_switcheroo_switch_ddc(pdev); + } if (drm_probe_ddc(adapter)) edid = (struct edid *)drm_do_get_edid(connector, adapter); + if (active_pdev) + vga_switcheroo_switch_ddc(active_pdev); + connector->display_info.raw_edid = (char *)edid; + mutex_unlock(_edid_mutex); return edid; } diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index e25cf31..e53f67d 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -205,6 +205,20 @@ find_active_client(struct list_head *head) return NULL; } +struct pci_dev *vga_switcheroo_get_active_client(void) +{ + struct vga_switcheroo_client *client; + struct pci_dev *pdev = NULL; + + mutex_lock(_mutex); + client = find_active_client(_priv.clients); + if (client) + pdev = client->pdev; + mutex_unlock(_mutex); + return pdev; +} +EXPORT_SYMBOL(vga_switcheroo_get_active_client); + int vga_switcheroo_get_client_state(struct pci_dev *pdev) { struct vga_switcheroo_client *client; @@ -252,6 +266,29 @@ 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 ret = 0; + int id; + + mutex_lock(_mutex); + + if (!vgasr_priv.handler) { + ret = -ENODEV; + goto out; + } + + if (vgasr_priv.handler->switch_ddc) { + id = vgasr_priv.handler->get_client_id(pdev); + ret = vgasr_priv.handler->switch_ddc(id); + } + +out: + mutex_unlock(_mutex); + return ret; +} +EXPORT_SYMBOL(vga_switcheroo_switch_ddc); + static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; @@ -342,9 +379,15 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, ); } + if (vgasr_priv.handler->switch_ddc) { + ret =
Re: [RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Mon, Aug 06, 2012 at 01:23:17PM +0100, Matthew Garrett wrote: On Sun, Aug 05, 2012 at 11:40:16PM +0200, Daniel Vetter wrote: As long as it's only apple shipping multi-gpu machines with broken/non-existing vbt, I'll happily stomach the quirk list entries. They're bad, but imo the lesser evil. Doing this via quirks means that we'll always be broken on the hardware at the point where it ships. Implementing the functionality means we stand some chance of working out of the box. I've been thinking some today about how this functionality might be implemented. It looks like the simplest way to let the inactive GPU have the i2c bus temporarily is to have drm_get_edid() control the mux and serialize it with a mutex. It should be pretty easy to make vga_switcheroo support muxing the DDC separately from the display. There is the problem of vga_switcheroo not really being operational until it has two clients and a handler, and the graphics drivers not registering clients until they've initialized LVDS. This probably won't be too dificult to solve. The bigger problem is still making sure the switcheroo handler is registered, when it's needed, before trying to read the EDID. We could potentially delay initializing non-active graphics devices until after a switcheroo handler has been registered, but that's a problem if the handler is implemented by the driver for the secondary GPU (is this ever the case?). Otherwise we seem to be stuck with making i915 able to cope with getting modes for LVDS after initialization, which kind of puts us back where we started. Any other ideas? ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Mon, Aug 06, 2012 at 01:23:17PM +0100, Matthew Garrett wrote: > On Sun, Aug 05, 2012 at 11:40:16PM +0200, Daniel Vetter wrote: > > > As long as it's only apple shipping multi-gpu machines with > > broken/non-existing vbt, I'll happily stomach the quirk list entries. > > They're bad, but imo the lesser evil. > > Doing this via quirks means that we'll always be broken on the hardware > at the point where it ships. Implementing the functionality means we > stand some chance of working out of the box. I've been thinking some today about how this functionality might be implemented. It looks like the simplest way to let the inactive GPU have the i2c bus temporarily is to have drm_get_edid() control the mux and serialize it with a mutex. It should be pretty easy to make vga_switcheroo support muxing the DDC separately from the display. There is the problem of vga_switcheroo not really being operational until it has two clients and a handler, and the graphics drivers not registering clients until they've initialized LVDS. This probably won't be too dificult to solve. The bigger problem is still making sure the switcheroo handler is registered, when it's needed, before trying to read the EDID. We could potentially delay initializing non-active graphics devices until after a switcheroo handler has been registered, but that's a problem if the handler is implemented by the driver for the secondary GPU (is this ever the case?). Otherwise we seem to be stuck with making i915 able to cope with getting modes for LVDS after initialization, which kind of puts us back where we started. Any other ideas?
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Sun, Aug 05, 2012 at 07:20:31PM -0400, Alex Deucher wrote: > On Sun, Aug 5, 2012 at 5:44 PM, Dave Airlie wrote: > > On Mon, Aug 6, 2012 at 7:40 AM, Daniel Vetter wrote: > >> On Sun, Aug 05, 2012 at 10:18:38PM +0100, Matthew Garrett wrote: > >>> On Sun, Aug 05, 2012 at 11:14:12PM +0200, Daniel Vetter wrote: > >>> > >>> > I like this approach more - the only other solution I see is to ask the > >>> > currently active driver (i.e. radeon) at bootime for the right mode. > >>> > Which > >>> > sounds much more hellish and fragile ... > >>> > >>> The "correct" approach is clearly to just have the drm core change the > >>> i2c mux before requesting edid, but that's made difficult because of the > >>> absence of ordering guarantees in initialisation. I don't like quirking > >>> this, since we're then back to the situation of potentially having to > >>> add every new piece of related hardware to the quirk list. > >> > >> The "correct" approach of switching the mux before we fetch the edid is > >> actualy the one I fear will result in fragile code: Only run on few > >> machines, and as you say with tons of funky interactions with the init > >> sequence ordering. And I guess people will bitch about the flickering > >> this will cause ;-) > >> > >> As long as it's only apple shipping multi-gpu machines with > >> broken/non-existing vbt, I'll happily stomach the quirk list entries. > >> They're bad, but imo the lesser evil. > > > > Well in theory you can switch the ddc lines without switching the other > > lines, > > so we could do a mutex protected mux switch around edid retrival, > > > > Depends on the system. On non-Macs, some systems have a single mux, > others have a separate mux for i2c and display as specified in the > ATPX ACPI methods. Not sure how the Macs do it. I've started > cleaning up the PX radeon code along with a bunch of other radeon > ralated ACPI fixes: > http://cgit.freedesktop.org/~agd5f/linux/log/?h=acpi_patches The Macs mux the i2c and display separately. However they don't support the vendor ACPI interfaces for the mux. The driver that provides the vga_switcheroo handler is separate from the graphics drivers, and the same whether the discrete graphics are Radeon or nVidia. Really to support this in any generic sort of way vga_switcheroo needs to support muxing the DDC separately from the display, but as Matthew pointed out the ordering of initialization could be a problem. Even if we protect the DDC with a mutex how can we guarantee that the switcheroo handler is registered to switch the DDC before i915 is ready to check for an EDID?
[RFC PATCH 0/5] i915 changes for hybrid graphics support on Macbooks
The following patches are part of a larger series I've been working on to get vga_switcheroo working on hybrid graphics Macbooks. Some of these machines are not providing a VBT, and since the LVDS panel is connected to the discrete GPU at boot we can't get a mode for the panel during initialization. As a result the LVDS connector is not registered with DRM, and graphics switching is not possible. These patches fix this issue by registering the connector even if we can't get a mode for the panel. If we don't have an EDID we check again from the vga_switcheroo reprobe callback. This is working, except for the framebuffer console which isn't displaying when switched to the integrated GPU, which I still need to debug. I'm not entirely sure whether or not this is the correct approach though, so I wanted to go ahead and get some feedback on the patches now to make sure I'm on the right track. Thanks, Seth Andreas Heider (1): drm/i915: Add support for vga_switcheroo reprobe Seth Forshee (4): drm/i915: separate out code to get EDID from LVDS panel drm/i915: register LVDS connector even if we can't get a panel mode drm/i915: make intel_lvds_get_edid() more robust drm/i915: check LVDS for EDID on GPU switches drivers/gpu/drm/i915/i915_dma.c |9 ++- drivers/gpu/drm/i915/intel_drv.h |1 + drivers/gpu/drm/i915/intel_lvds.c | 156 + 3 files changed, 97 insertions(+), 69 deletions(-) ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 1/5] drm/i915: Add support for vga_switcheroo reprobe
From: Andreas Heider andr...@meetr.de Signed-off-by: Andreas Heider andr...@meetr.de --- drivers/gpu/drm/i915/i915_dma.c |8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9cf7dfe..5b5176d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1263,6 +1263,12 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ } } +static void i915_switcheroo_reprobe(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + intel_fb_output_poll_changed(dev); +} + static bool i915_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -1276,7 +1282,7 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) 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, }; -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 2/5] drm/i915: separate out code to get EDID from LVDS panel
This code will be reused to support hybrid graphics on some Apple machines that can't get a mode for the LVDS panel at boot, so move it into a new function named intel_lvds_get_edid(). Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/i915/intel_lvds.c | 95 + 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e05c0d3..5069137 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -46,6 +46,7 @@ struct intel_lvds { struct edid *edid; + u8 i2c_pin; int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; @@ -897,6 +898,54 @@ static bool intel_lvds_supported(struct drm_device *dev) return IS_MOBILE(dev) !IS_I830(dev); } +static bool intel_lvds_get_edid(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev-dev_private; + struct drm_connector *connector = dev_priv-int_lvds_connector; + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct drm_display_mode *scan; /* *modes, *bios_mode; */ + + /* +* Attempt to get the fixed panel mode from DDC. Assume that the +* preferred mode is the right one. +*/ + intel_lvds-edid = drm_get_edid(connector, + intel_gmbus_get_adapter(dev_priv, + intel_lvds-i2c_pin)); + if (intel_lvds-edid) { + if (drm_add_edid_modes(connector, + intel_lvds-edid)) { + drm_mode_connector_update_edid_property(connector, + intel_lvds-edid); + } else { + kfree(intel_lvds-edid); + intel_lvds-edid = NULL; + } + } + if (!intel_lvds-edid) { + /* Didn't get an EDID, so +* Set wide sync ranges so we get all modes +* handed to valid_mode for checking +*/ + connector-display_info.min_vfreq = 0; + connector-display_info.max_vfreq = 200; + connector-display_info.min_hfreq = 0; + connector-display_info.max_hfreq = 200; + } + + list_for_each_entry(scan, connector-probed_modes, head) { + if (scan-type DRM_MODE_TYPE_PREFERRED) { + intel_lvds-fixed_mode = + drm_mode_duplicate(dev, scan); + intel_find_lvds_downclock(dev, + intel_lvds-fixed_mode, + connector); + return true; + } + } + return false; +} + /** * intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -912,7 +961,6 @@ bool intel_lvds_init(struct drm_device *dev) struct intel_connector *intel_connector; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; u32 lvds; int pipe; @@ -955,9 +1003,11 @@ bool intel_lvds_init(struct drm_device *dev) intel_lvds-pfit_control = I915_READ(PFIT_CONTROL); } + intel_lvds-i2c_pin = pin; intel_encoder = intel_lvds-base; encoder = intel_encoder-base; connector = intel_connector-base; + dev_priv-int_lvds_connector = connector; drm_connector_init(dev, intel_connector-base, intel_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); @@ -991,6 +1041,7 @@ bool intel_lvds_init(struct drm_device *dev) dev-mode_config.scaling_mode_property, DRM_MODE_SCALE_ASPECT); intel_lvds-fitting_mode = DRM_MODE_SCALE_ASPECT; + /* * LVDS discovery: * 1) check for EDID on DDC @@ -1001,44 +1052,8 @@ bool intel_lvds_init(struct drm_device *dev) *if closed, act like it's not there for now */ - /* -* Attempt to get the fixed panel mode from DDC. Assume that the -* preferred mode is the right one. -*/ - intel_lvds-edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - pin)); - if (intel_lvds-edid) { - if (drm_add_edid_modes(connector, - intel_lvds-edid)) { - drm_mode_connector_update_edid_property(connector, - intel_lvds-edid); - } else
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
Some Apple hybrid graphics machines do not have the LVDS panel connected to the integrated GPU at boot and also do not supply a VBT. The LVDS connector is not registered as a result, making it impossible to support graphics switching. This patch changes intel_lvds_init() to register the connector even if we can't find any panel modes. This makes it necessary to always check intel_lvds-fixed_mode before use, as it could now be NULL. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/i915/intel_lvds.c | 48 +++-- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5069137..c1ab632 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -161,6 +161,8 @@ static int intel_lvds_mode_valid(struct drm_connector *connector, struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_display_mode *fixed_mode = intel_lvds-fixed_mode; + if (!fixed_mode) + return MODE_PANEL; if (mode-hdisplay fixed_mode-hdisplay) return MODE_PANEL; if (mode-vdisplay fixed_mode-vdisplay) @@ -262,7 +264,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - intel_fixed_panel_mode(intel_lvds-fixed_mode, adjusted_mode); + if (intel_lvds-fixed_mode) + intel_fixed_panel_mode(intel_lvds-fixed_mode, adjusted_mode); if (HAS_PCH_SPLIT(dev)) { intel_pch_panel_fitting(dev, intel_lvds-fitting_mode, @@ -461,12 +464,13 @@ static int intel_lvds_get_modes(struct drm_connector *connector) { struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector-dev; - struct drm_display_mode *mode; + struct drm_display_mode *mode = NULL; if (intel_lvds-edid) return drm_add_edid_modes(connector, intel_lvds-edid); - mode = drm_mode_duplicate(dev, intel_lvds-fixed_mode); + if (intel_lvds-fixed_mode) + mode = drm_mode_duplicate(dev, intel_lvds-fixed_mode); if (mode == NULL) return 0; @@ -1073,26 +1077,21 @@ bool intel_lvds_init(struct drm_device *dev) */ /* Ironlake: FIXME if still fail, not try pipe mode now */ - if (HAS_PCH_SPLIT(dev)) - goto failed; - - lvds = I915_READ(LVDS); - pipe = (lvds LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = intel_get_crtc_for_pipe(dev, pipe); - - if (crtc (lvds LVDS_PORT_EN)) { - intel_lvds-fixed_mode = intel_crtc_mode_get(dev, crtc); - if (intel_lvds-fixed_mode) { - intel_lvds-fixed_mode-type |= - DRM_MODE_TYPE_PREFERRED; - goto out; + if (!HAS_PCH_SPLIT(dev)) { + lvds = I915_READ(LVDS); + pipe = (lvds LVDS_PIPEB_SELECT) ? 1 : 0; + crtc = intel_get_crtc_for_pipe(dev, pipe); + + if (crtc (lvds LVDS_PORT_EN)) { + intel_lvds-fixed_mode = intel_crtc_mode_get(dev, crtc); + if (intel_lvds-fixed_mode) { + intel_lvds-fixed_mode-type |= + DRM_MODE_TYPE_PREFERRED; + goto out; + } } } - /* If we still don't have a mode after all that, give up. */ - if (!intel_lvds-fixed_mode) - goto failed; - out: /* * Unlock registers and just @@ -1116,13 +1115,4 @@ out: intel_panel_setup_backlight(dev); return true; - -failed: - DRM_DEBUG_KMS(No LVDS modes found, disabling.\n); - dev_priv-int_lvds_connector = NULL; - drm_connector_cleanup(connector); - drm_encoder_cleanup(encoder); - kfree(intel_lvds); - kfree(intel_connector); - return false; } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 4/5] drm/i915: make intel_lvds_get_edid() more robust
intel_lvds_get_edid() needs to be called when switching GPUs, but it's currently making assumptions that it will only be called once and that there's always an LVDS connector present when it's called. Fix these assumptions. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/i915/intel_lvds.c | 17 - 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c1ab632..9d05a90 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -906,9 +906,18 @@ static bool intel_lvds_get_edid(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev-dev_private; struct drm_connector *connector = dev_priv-int_lvds_connector; - struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct intel_lvds *intel_lvds; struct drm_display_mode *scan; /* *modes, *bios_mode; */ + if (!connector) + return false; + + intel_lvds = intel_attached_lvds(connector); + + /* If we already have an EDID, no need to check again */ + if (intel_lvds-edid) + return true; + /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. @@ -939,6 +948,12 @@ static bool intel_lvds_get_edid(struct drm_device *dev) list_for_each_entry(scan, connector-probed_modes, head) { if (scan-type DRM_MODE_TYPE_PREFERRED) { + /* +* If we already have a preferred mode from another +* source, prefer the one from the EDID. +*/ + if (intel_lvds-fixed_mode) + drm_mode_destroy(dev, intel_lvds-fixed_mode); intel_lvds-fixed_mode = drm_mode_duplicate(dev, scan); intel_find_lvds_downclock(dev, -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[RFC PATCH 5/5] drm/i915: check LVDS for EDID on GPU switches
If the LVDS panel wasn't connected at boot then we won't have an EDID for it. To fix this, call intel_lvds_get_edid() from the vga_switcheroo reprobe callback. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/i915/i915_dma.c |1 + drivers/gpu/drm/i915/intel_drv.h |1 + drivers/gpu/drm/i915/intel_lvds.c |2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5b5176d..c9c942e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1266,6 +1266,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ static void i915_switcheroo_reprobe(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + intel_lvds_get_edid(dev); intel_fb_output_poll_changed(dev); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8435355..bcc14f9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -356,6 +356,7 @@ extern void intel_dvo_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev); extern void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj); +extern bool intel_lvds_get_edid(struct drm_device *dev); extern bool intel_lvds_init(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int dp_reg); void diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 9d05a90..39dbefc 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -902,7 +902,7 @@ static bool intel_lvds_supported(struct drm_device *dev) return IS_MOBILE(dev) !IS_I830(dev); } -static bool intel_lvds_get_edid(struct drm_device *dev) +bool intel_lvds_get_edid(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev-dev_private; struct drm_connector *connector = dev_priv-int_lvds_connector; -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Fri, Aug 03, 2012 at 05:14:16PM +0100, Matthew Garrett wrote: On Fri, Aug 03, 2012 at 11:02:19AM -0500, Seth Forshee wrote: Some Apple hybrid graphics machines do not have the LVDS panel connected to the integrated GPU at boot and also do not supply a VBT. The LVDS connector is not registered as a result, making it impossible to support graphics switching. This patch changes intel_lvds_init() to register the connector even if we can't find any panel modes. This makes it necessary to always check intel_lvds-fixed_mode before use, as it could now be NULL. This one kind of sucks. I think adding a quirk for this situation would be justifiable, rather than doing it for all devices. This is one of the things I wasn't so sure about. There are various checks in intel_lvds_init() that can cause it to bail out before we try to get the EDID, and I don't fully understand all of them. If non-laptop machines are expected to bail out before the EDID check then the approach I've taken seems reasonable. Otherwise adding a quirk probably is a good idea. Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Fri, Aug 03, 2012 at 05:27:02PM +0100, Matthew Garrett wrote: On Fri, Aug 03, 2012 at 11:24:51AM -0500, Seth Forshee wrote: This is one of the things I wasn't so sure about. There are various checks in intel_lvds_init() that can cause it to bail out before we try to get the EDID, and I don't fully understand all of them. If non-laptop machines are expected to bail out before the EDID check then the approach I've taken seems reasonable. Otherwise adding a quirk probably is a good idea. I know we've previously had problems with machines with phantom LVDS hardware, but I'm not sure what the current state of affairs is. It turns out that the framebuffer console issue is due to not having a mode when initializing LVDS. As a result 1024x768 is getting used for the framebuffer. So quirking is going to fix this situation. In fact, with the changes below switcheroo seems to work perfectly, without any of these patches at all. diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 627fe35..d83e5bc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -503,6 +503,7 @@ typedef struct drm_i915_private { enum intel_pch pch_type; unsigned long quirks; + struct drm_display_mode *lvds_quirk_mode; /* Register state */ bool modeset_on_lid; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f615976..c810177 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7069,7 +7069,7 @@ static void intel_init_display(struct drm_device *dev) * resume, or other times. This quirk makes sure that's the case for * affected systems. */ -static void quirk_pipea_force(struct drm_device *dev) +static void quirk_pipea_force(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev-dev_private; @@ -7080,7 +7080,7 @@ static void quirk_pipea_force(struct drm_device *dev) /* * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason */ -static void quirk_ssc_force_disable(struct drm_device *dev) +static void quirk_ssc_force_disable(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev-dev_private; dev_priv-quirks |= QUIRK_LVDS_SSC_DISABLE; @@ -7091,48 +7091,74 @@ static void quirk_ssc_force_disable(struct drm_device *dev) * A machine (e.g. Acer Aspire 5734Z) may need to invert the panel backlight * brightness value */ -static void quirk_invert_brightness(struct drm_device *dev) +static void quirk_invert_brightness(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev-dev_private; dev_priv-quirks |= QUIRK_INVERT_BRIGHTNESS; DRM_INFO(applying inverted panel brightness quirk\n); } +/* + * Some machines (e.g. certain Macbooks) may not be able to determine the + * LVDS mode during driver initialization + */ +static void quirk_lvds_panel_mode(struct drm_device *dev, void *data) +{ + struct drm_i915_private *dev_priv = dev-dev_private; + dev_priv-lvds_quirk_mode = data; + DRM_INFO(applying LVDS panel mode quirk: %p\n, data); +} + +/* LVDS panel mode for Macbook Pro 8,2 */ +struct drm_display_mode mbp82_panel_mode = { + DRM_MODE(1440x900, DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, 1520, +1600, 0, 900, 903, 909, 926, 0, +DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC) +}; + struct intel_quirk { int device; int subsystem_vendor; int subsystem_device; - void (*hook)(struct drm_device *dev); + void (*hook)(struct drm_device *dev, void *data); + void *hook_data; }; static struct intel_quirk intel_quirks[] = { /* HP Mini needs pipe A force quirk (LP: #322104) */ - { 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, + { 0x27ae, 0x103c, 0x361a, quirk_pipea_force, NULL }, /* Thinkpad R31 needs pipe A force quirk */ - { 0x3577, 0x1014, 0x0505, quirk_pipea_force }, + { 0x3577, 0x1014, 0x0505, quirk_pipea_force, NULL }, /* Toshiba Protege R-205, S-209 needs pipe A force quirk */ - { 0x2592, 0x1179, 0x0001, quirk_pipea_force }, + { 0x2592, 0x1179, 0x0001, quirk_pipea_force, NULL }, /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */ - { 0x3577, 0x1014, 0x0513, quirk_pipea_force }, + { 0x3577, 0x1014, 0x0513, quirk_pipea_force, NULL }, /* ThinkPad X40 needs pipe A force quirk */ /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ - { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, + { 0x2782, 0x17aa, 0x201a, quirk_pipea_force, NULL }, /* 855 before need to leave pipe A dpll A up */ - { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, - { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, + { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force, NULL
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Fri, Aug 03, 2012 at 05:27:02PM +0100, Matthew Garrett wrote: > On Fri, Aug 03, 2012 at 11:24:51AM -0500, Seth Forshee wrote: > > > This is one of the things I wasn't so sure about. There are various > > checks in intel_lvds_init() that can cause it to bail out before we try > > to get the EDID, and I don't fully understand all of them. If non-laptop > > machines are expected to bail out before the EDID check then the > > approach I've taken seems reasonable. Otherwise adding a quirk probably > > is a good idea. > > I know we've previously had problems with machines with phantom LVDS > hardware, but I'm not sure what the current state of affairs is. It turns out that the framebuffer console issue is due to not having a mode when initializing LVDS. As a result 1024x768 is getting used for the framebuffer. So quirking is going to fix this situation. In fact, with the changes below switcheroo seems to work perfectly, without any of these patches at all. diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 627fe35..d83e5bc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -503,6 +503,7 @@ typedef struct drm_i915_private { enum intel_pch pch_type; unsigned long quirks; + struct drm_display_mode *lvds_quirk_mode; /* Register state */ bool modeset_on_lid; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f615976..c810177 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7069,7 +7069,7 @@ static void intel_init_display(struct drm_device *dev) * resume, or other times. This quirk makes sure that's the case for * affected systems. */ -static void quirk_pipea_force(struct drm_device *dev) +static void quirk_pipea_force(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -7080,7 +7080,7 @@ static void quirk_pipea_force(struct drm_device *dev) /* * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason */ -static void quirk_ssc_force_disable(struct drm_device *dev) +static void quirk_ssc_force_disable(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev->dev_private; dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE; @@ -7091,48 +7091,74 @@ static void quirk_ssc_force_disable(struct drm_device *dev) * A machine (e.g. Acer Aspire 5734Z) may need to invert the panel backlight * brightness value */ -static void quirk_invert_brightness(struct drm_device *dev) +static void quirk_invert_brightness(struct drm_device *dev, void *data) { struct drm_i915_private *dev_priv = dev->dev_private; dev_priv->quirks |= QUIRK_INVERT_BRIGHTNESS; DRM_INFO("applying inverted panel brightness quirk\n"); } +/* + * Some machines (e.g. certain Macbooks) may not be able to determine the + * LVDS mode during driver initialization + */ +static void quirk_lvds_panel_mode(struct drm_device *dev, void *data) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + dev_priv->lvds_quirk_mode = data; + DRM_INFO("applying LVDS panel mode quirk: %p\n", data); +} + +/* LVDS panel mode for Macbook Pro 8,2 */ +struct drm_display_mode mbp82_panel_mode = { + DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, 1520, +1600, 0, 900, 903, 909, 926, 0, +DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC) +}; + struct intel_quirk { int device; int subsystem_vendor; int subsystem_device; - void (*hook)(struct drm_device *dev); + void (*hook)(struct drm_device *dev, void *data); + void *hook_data; }; static struct intel_quirk intel_quirks[] = { /* HP Mini needs pipe A force quirk (LP: #322104) */ - { 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, + { 0x27ae, 0x103c, 0x361a, quirk_pipea_force, NULL }, /* Thinkpad R31 needs pipe A force quirk */ - { 0x3577, 0x1014, 0x0505, quirk_pipea_force }, + { 0x3577, 0x1014, 0x0505, quirk_pipea_force, NULL }, /* Toshiba Protege R-205, S-209 needs pipe A force quirk */ - { 0x2592, 0x1179, 0x0001, quirk_pipea_force }, + { 0x2592, 0x1179, 0x0001, quirk_pipea_force, NULL }, /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */ - { 0x3577, 0x1014, 0x0513, quirk_pipea_force }, + { 0x3577, 0x1014, 0x0513, quirk_pipea_force, NULL }, /* ThinkPad X40 needs pipe A force quirk */ /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ - { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, + { 0x2782, 0x17aa, 0x201a, quirk_pipea_force, NULL }, /* 855 & before need to leave pipe A & dpll A up */ - { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_fo
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
On Fri, Aug 03, 2012 at 05:14:16PM +0100, Matthew Garrett wrote: > On Fri, Aug 03, 2012 at 11:02:19AM -0500, Seth Forshee wrote: > > Some Apple hybrid graphics machines do not have the LVDS panel connected > > to the integrated GPU at boot and also do not supply a VBT. The LVDS > > connector is not registered as a result, making it impossible to support > > graphics switching. > > > > This patch changes intel_lvds_init() to register the connector even if > > we can't find any panel modes. This makes it necessary to always check > > intel_lvds->fixed_mode before use, as it could now be NULL. > > This one kind of sucks. I think adding a quirk for this situation would > be justifiable, rather than doing it for all devices. This is one of the things I wasn't so sure about. There are various checks in intel_lvds_init() that can cause it to bail out before we try to get the EDID, and I don't fully understand all of them. If non-laptop machines are expected to bail out before the EDID check then the approach I've taken seems reasonable. Otherwise adding a quirk probably is a good idea. Seth
[RFC PATCH 5/5] drm/i915: check LVDS for EDID on GPU switches
If the LVDS panel wasn't connected at boot then we won't have an EDID for it. To fix this, call intel_lvds_get_edid() from the vga_switcheroo reprobe callback. Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/i915_dma.c |1 + drivers/gpu/drm/i915/intel_drv.h |1 + drivers/gpu/drm/i915/intel_lvds.c |2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5b5176d..c9c942e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1266,6 +1266,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ static void i915_switcheroo_reprobe(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + intel_lvds_get_edid(dev); intel_fb_output_poll_changed(dev); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8435355..bcc14f9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -356,6 +356,7 @@ extern void intel_dvo_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev); extern void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj); +extern bool intel_lvds_get_edid(struct drm_device *dev); extern bool intel_lvds_init(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int dp_reg); void diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 9d05a90..39dbefc 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -902,7 +902,7 @@ static bool intel_lvds_supported(struct drm_device *dev) return IS_MOBILE(dev) && !IS_I830(dev); } -static bool intel_lvds_get_edid(struct drm_device *dev) +bool intel_lvds_get_edid(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector = dev_priv->int_lvds_connector; -- 1.7.9.5
[RFC PATCH 4/5] drm/i915: make intel_lvds_get_edid() more robust
intel_lvds_get_edid() needs to be called when switching GPUs, but it's currently making assumptions that it will only be called once and that there's always an LVDS connector present when it's called. Fix these assumptions. Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/intel_lvds.c | 17 - 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c1ab632..9d05a90 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -906,9 +906,18 @@ static bool intel_lvds_get_edid(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector = dev_priv->int_lvds_connector; - struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct intel_lvds *intel_lvds; struct drm_display_mode *scan; /* *modes, *bios_mode; */ + if (!connector) + return false; + + intel_lvds = intel_attached_lvds(connector); + + /* If we already have an EDID, no need to check again */ + if (intel_lvds->edid) + return true; + /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. @@ -939,6 +948,12 @@ static bool intel_lvds_get_edid(struct drm_device *dev) list_for_each_entry(scan, >probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { + /* +* If we already have a preferred mode from another +* source, prefer the one from the EDID. +*/ + if (intel_lvds->fixed_mode) + drm_mode_destroy(dev, intel_lvds->fixed_mode); intel_lvds->fixed_mode = drm_mode_duplicate(dev, scan); intel_find_lvds_downclock(dev, -- 1.7.9.5
[RFC PATCH 3/5] drm/i915: register LVDS connector even if we can't get a panel mode
Some Apple hybrid graphics machines do not have the LVDS panel connected to the integrated GPU at boot and also do not supply a VBT. The LVDS connector is not registered as a result, making it impossible to support graphics switching. This patch changes intel_lvds_init() to register the connector even if we can't find any panel modes. This makes it necessary to always check intel_lvds->fixed_mode before use, as it could now be NULL. Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/intel_lvds.c | 48 +++-- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5069137..c1ab632 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -161,6 +161,8 @@ static int intel_lvds_mode_valid(struct drm_connector *connector, struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode; + if (!fixed_mode) + return MODE_PANEL; if (mode->hdisplay > fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > fixed_mode->vdisplay) @@ -262,7 +264,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode); + if (intel_lvds->fixed_mode) + intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode); if (HAS_PCH_SPLIT(dev)) { intel_pch_panel_fitting(dev, intel_lvds->fitting_mode, @@ -461,12 +464,13 @@ static int intel_lvds_get_modes(struct drm_connector *connector) { struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - struct drm_display_mode *mode; + struct drm_display_mode *mode = NULL; if (intel_lvds->edid) return drm_add_edid_modes(connector, intel_lvds->edid); - mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); + if (intel_lvds->fixed_mode) + mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); if (mode == NULL) return 0; @@ -1073,26 +1077,21 @@ bool intel_lvds_init(struct drm_device *dev) */ /* Ironlake: FIXME if still fail, not try pipe mode now */ - if (HAS_PCH_SPLIT(dev)) - goto failed; - - lvds = I915_READ(LVDS); - pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = intel_get_crtc_for_pipe(dev, pipe); - - if (crtc && (lvds & LVDS_PORT_EN)) { - intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc); - if (intel_lvds->fixed_mode) { - intel_lvds->fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - goto out; + if (!HAS_PCH_SPLIT(dev)) { + lvds = I915_READ(LVDS); + pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; + crtc = intel_get_crtc_for_pipe(dev, pipe); + + if (crtc && (lvds & LVDS_PORT_EN)) { + intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc); + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; + } } } - /* If we still don't have a mode after all that, give up. */ - if (!intel_lvds->fixed_mode) - goto failed; - out: /* * Unlock registers and just @@ -1116,13 +1115,4 @@ out: intel_panel_setup_backlight(dev); return true; - -failed: - DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); - dev_priv->int_lvds_connector = NULL; - drm_connector_cleanup(connector); - drm_encoder_cleanup(encoder); - kfree(intel_lvds); - kfree(intel_connector); - return false; } -- 1.7.9.5
[RFC PATCH 2/5] drm/i915: separate out code to get EDID from LVDS panel
This code will be reused to support hybrid graphics on some Apple machines that can't get a mode for the LVDS panel at boot, so move it into a new function named intel_lvds_get_edid(). Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/intel_lvds.c | 95 + 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e05c0d3..5069137 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -46,6 +46,7 @@ struct intel_lvds { struct edid *edid; + u8 i2c_pin; int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; @@ -897,6 +898,54 @@ static bool intel_lvds_supported(struct drm_device *dev) return IS_MOBILE(dev) && !IS_I830(dev); } +static bool intel_lvds_get_edid(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_connector *connector = dev_priv->int_lvds_connector; + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct drm_display_mode *scan; /* *modes, *bios_mode; */ + + /* +* Attempt to get the fixed panel mode from DDC. Assume that the +* preferred mode is the right one. +*/ + intel_lvds->edid = drm_get_edid(connector, + intel_gmbus_get_adapter(dev_priv, + intel_lvds->i2c_pin)); + if (intel_lvds->edid) { + if (drm_add_edid_modes(connector, + intel_lvds->edid)) { + drm_mode_connector_update_edid_property(connector, + intel_lvds->edid); + } else { + kfree(intel_lvds->edid); + intel_lvds->edid = NULL; + } + } + if (!intel_lvds->edid) { + /* Didn't get an EDID, so +* Set wide sync ranges so we get all modes +* handed to valid_mode for checking +*/ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + } + + list_for_each_entry(scan, >probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + intel_lvds->fixed_mode = + drm_mode_duplicate(dev, scan); + intel_find_lvds_downclock(dev, + intel_lvds->fixed_mode, + connector); + return true; + } + } + return false; +} + /** * intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -912,7 +961,6 @@ bool intel_lvds_init(struct drm_device *dev) struct intel_connector *intel_connector; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; u32 lvds; int pipe; @@ -955,9 +1003,11 @@ bool intel_lvds_init(struct drm_device *dev) intel_lvds->pfit_control = I915_READ(PFIT_CONTROL); } + intel_lvds->i2c_pin = pin; intel_encoder = _lvds->base; encoder = _encoder->base; connector = _connector->base; + dev_priv->int_lvds_connector = connector; drm_connector_init(dev, _connector->base, _lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); @@ -991,6 +1041,7 @@ bool intel_lvds_init(struct drm_device *dev) dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_ASPECT); intel_lvds->fitting_mode = DRM_MODE_SCALE_ASPECT; + /* * LVDS discovery: * 1) check for EDID on DDC @@ -1001,44 +1052,8 @@ bool intel_lvds_init(struct drm_device *dev) *if closed, act like it's not there for now */ - /* -* Attempt to get the fixed panel mode from DDC. Assume that the -* preferred mode is the right one. -*/ - intel_lvds->edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - pin)); - if (intel_lvds->edid) { - if (drm_add_edid_modes(connector, - intel_lvds->edid)) { - drm_mode_connector_update_edid_property(connector, -
[RFC PATCH 1/5] drm/i915: Add support for vga_switcheroo reprobe
From: Andreas HeiderSigned-off-by: Andreas Heider --- drivers/gpu/drm/i915/i915_dma.c |8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9cf7dfe..5b5176d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1263,6 +1263,12 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ } } +static void i915_switcheroo_reprobe(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + intel_fb_output_poll_changed(dev); +} + static bool i915_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -1276,7 +1282,7 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) 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, }; -- 1.7.9.5
[RFC PATCH 0/5] i915 changes for hybrid graphics support on Macbooks
The following patches are part of a larger series I've been working on to get vga_switcheroo working on hybrid graphics Macbooks. Some of these machines are not providing a VBT, and since the LVDS panel is connected to the discrete GPU at boot we can't get a mode for the panel during initialization. As a result the LVDS connector is not registered with DRM, and graphics switching is not possible. These patches fix this issue by registering the connector even if we can't get a mode for the panel. If we don't have an EDID we check again from the vga_switcheroo reprobe callback. This is working, except for the framebuffer console which isn't displaying when switched to the integrated GPU, which I still need to debug. I'm not entirely sure whether or not this is the correct approach though, so I wanted to go ahead and get some feedback on the patches now to make sure I'm on the right track. Thanks, Seth Andreas Heider (1): drm/i915: Add support for vga_switcheroo reprobe Seth Forshee (4): drm/i915: separate out code to get EDID from LVDS panel drm/i915: register LVDS connector even if we can't get a panel mode drm/i915: make intel_lvds_get_edid() more robust drm/i915: check LVDS for EDID on GPU switches drivers/gpu/drm/i915/i915_dma.c |9 ++- drivers/gpu/drm/i915/intel_drv.h |1 + drivers/gpu/drm/i915/intel_lvds.c | 156 + 3 files changed, 97 insertions(+), 69 deletions(-)
[PATCH v2] drm/i915: ignore pipe select bit when checking for LVDS register initialization
The Lenovo Thinkpad T410 has the LVDS_PIPEB_SELECT bit set in the LVDS register when booted with the lid closed, even though the LVDS hasn't really been initialized. Ignore this bit so that the VBT value will be used instead. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/i915/intel_display.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e0aa064..ae17526 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -434,7 +434,7 @@ static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, * register is uninitialized. */ val = I915_READ(reg); - if (!(val ~LVDS_DETECTED)) + if (!(val ~(LVDS_PIPE_MASK | LVDS_DETECTED))) val = dev_priv-bios_lvds_val; dev_priv-lvds_val = val; } -- 1.7.9.5 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v2] drm/i915: ignore pipe select bit when checking for LVDS register initialization
On Wed, Jun 13, 2012 at 09:46:15PM +0200, Daniel Vetter wrote: On Wed, Jun 13, 2012 at 01:46:58PM -0500, Seth Forshee wrote: The Lenovo Thinkpad T410 has the LVDS_PIPEB_SELECT bit set in the LVDS register when booted with the lid closed, even though the LVDS hasn't really been initialized. Ignore this bit so that the VBT value will be used instead. Signed-off-by: Seth Forshee seth.fors...@canonical.com Queued for -next, thanks for the patch. Chris had some reservations about the sanity of this patch, but given that it works around bios-insanity I'm gonna just take this chance to stab myself with lvds-machines blowing up left and right ;-) Let's hope that doesn't happen ;) I do find myself wondering though whether it might be better to prefer the value from the VBT whenever there's one available, and only rely on the actual register value as a fallback, since the bios can't be trusted to initialize the register. I'm pretty ignorant about all this graphics stuff though; I assume there's a reason it isn't done this way? Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2] drm/i915: ignore pipe select bit when checking for LVDS register initialization
On Wed, Jun 13, 2012 at 09:46:15PM +0200, Daniel Vetter wrote: > On Wed, Jun 13, 2012 at 01:46:58PM -0500, Seth Forshee wrote: > > The Lenovo Thinkpad T410 has the LVDS_PIPEB_SELECT bit set in the LVDS > > register when booted with the lid closed, even though the LVDS hasn't > > really been initialized. Ignore this bit so that the VBT value will be > > used instead. > > > > Signed-off-by: Seth Forshee > Queued for -next, thanks for the patch. Chris had some reservations about > the sanity of this patch, but given that it works around bios-insanity I'm > gonna just take this chance to stab myself with lvds-machines blowing up > left and right ;-) Let's hope that doesn't happen ;) I do find myself wondering though whether it might be better to prefer the value from the VBT whenever there's one available, and only rely on the actual register value as a fallback, since the bios can't be trusted to initialize the register. I'm pretty ignorant about all this graphics stuff though; I assume there's a reason it isn't done this way? Seth
[PATCH v2] drm/i915: ignore pipe select bit when checking for LVDS register initialization
The Lenovo Thinkpad T410 has the LVDS_PIPEB_SELECT bit set in the LVDS register when booted with the lid closed, even though the LVDS hasn't really been initialized. Ignore this bit so that the VBT value will be used instead. Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/intel_display.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e0aa064..ae17526 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -434,7 +434,7 @@ static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, * register is uninitialized. */ val = I915_READ(reg); - if (!(val & ~LVDS_DETECTED)) + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) val = dev_priv->bios_lvds_val; dev_priv->lvds_val = val; } -- 1.7.9.5
i915: lvds panel always blank when booted with lid closed
When I boot my Thinkpad T410 in a docking station with the lid closed, the lvds panel remains blank even when this output is active. This happens up to and including 3.5-rc2. I've determined that this happens because lvds isn't being initialized by the bios when I boot this way, and booting with lvds_channel_mode=2 fixes the issue. I see that there's logic in is_dual_link_lvds() intended to detect this situation, but it's failing because the T410 has the LVDS_PIPEB_SELECT bit set. The simple patch below fixes my machine by masking off this bit when determining whether or not lvds was initialized by the bios. I'm not sure though whether or not it's correct to expect that this bit might be set when lvds hasn't been initialized. The alternative seems to be quirking this machine as is done for some Macbooks. What is the correct solution? Thanks, Seth
[PATCH] drm/i915: ignore LVDS_PIPEB_SELECT when checking for LVDS register initialization
The Lenovo Thinkpad T410 has this bit set in the LVDS register when booted with the lid closed, even though the LVDS hasn't really been initialized. Ignore this bit so that the VBT value will be used instead. Signed-off-by: Seth Forshee --- drivers/gpu/drm/i915/intel_display.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e0aa064..f81f249 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -434,7 +434,7 @@ static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, * register is uninitialized. */ val = I915_READ(reg); - if (!(val & ~LVDS_DETECTED)) + if (!(val & ~(LVDS_PIPEB_SELECT | LVDS_DETECTED))) val = dev_priv->bios_lvds_val; dev_priv->lvds_val = val; }
[PATCH] drm/radeon/kms: disable output polling when suspended
Polling the outputs when the device is suspended can result in erroneous status updates. Disable output polling during suspend to prevent this from happening. Signed-off-by: Seth Forshee Reviewed-by: Alex Deucher Cc: stable at vger.kernel.org --- drivers/gpu/drm/radeon/radeon_device.c |4 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index cec51a5..49f7cb7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -883,6 +883,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, >mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -972,6 +974,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, >mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } -- 1.7.8.3
[PATCH] drm/radeon/kms: disable output polling when suspended
Polling the outputs when the device is suspended can result in erroneous status updates. Disable output polling during suspend to prevent this from happening. Signed-off-by: Seth Forshee seth.fors...@canonical.com Reviewed-by: Alex Deucher alexander.deuc...@amd.com Cc: sta...@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_device.c |4 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index cec51a5..49f7cb7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -883,6 +883,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev-switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, dev-mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -972,6 +974,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, dev-mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } -- 1.7.8.3 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 05:08:31PM -0600, Seth Forshee wrote: > > Can you track down who is calling the connector->detect() callbacks > > during suspend and resume? > > I got two different stack traces, see below. > > And to slightly amend my statement above, I'm only seeing the wrong > status returned on the suspend side of things. The status during resume > always seems to be correct. > > Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu > Call Trace: > [] radeon_dp_detect+0x1de/0x230 [radeon] > [] output_poll_execute+0xbd/0x1a0 [drm_kms_helper] > [] ? drm_helper_mode_fill_fb_struct+0x30/0x30 > [drm_kms_helper] > [] process_one_work+0x11a/0x480 > [] worker_thread+0x164/0x370 > [] ? manage_workers.isra.30+0x130/0x130 > [] kthread+0x8c/0xa0 > [] kernel_thread_helper+0x4/0x10 > [] ? flush_kthread_worker+0xa0/0xa0 > [] ? gs_change+0x13/0x13 > > Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu > Call Trace: > [] radeon_dp_detect+0x1de/0x230 [radeon] > [] drm_helper_probe_single_connector_modes+0x2c3/0x380 > [drm_kms_helper] > [] drm_fb_helper_hotplug_event+0xf2/0x150 [drm_kms_helper] > [] radeon_fb_output_poll_changed+0x15/0x20 [radeon] > [] radeon_output_poll_changed+0x15/0x20 [radeon] > [] output_poll_execute+0x190/0x1a0 [drm_kms_helper] > [] ? drm_helper_mode_fill_fb_struct+0x30/0x30 > [drm_kms_helper] > [] process_one_work+0x11a/0x480 > [] worker_thread+0x164/0x370 > [] ? manage_workers.isra.30+0x130/0x130 > [] kthread+0x8c/0xa0 > [] kernel_thread_helper+0x4/0x10 > [] ? flush_kthread_worker+0xa0/0xa0 > [] ? gs_change+0x13/0x13
[PATCH 1/1] drm/radeon/kms: disable output polling when suspended
Polling the outputs when the device is suspended can result in erroneous status updates. Disable output polling during suspend to prevent this from happening. Signed-off-by: Seth Forshee --- drivers/gpu/drm/radeon/radeon_device.c |4 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index cec51a5..49f7cb7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -883,6 +883,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, >mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -972,6 +974,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, >mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } -- 1.7.8.3
Re: radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 05:08:31PM -0600, Seth Forshee wrote: Can you track down who is calling the connector-detect() callbacks during suspend and resume? I got two different stack traces, see below. And to slightly amend my statement above, I'm only seeing the wrong status returned on the suspend side of things. The status during resume always seems to be correct. Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [a03f5e1e] radeon_dp_detect+0x1de/0x230 [radeon] [a014ac6d] output_poll_execute+0xbd/0x1a0 [drm_kms_helper] [a014abb0] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [81083dca] process_one_work+0x11a/0x480 [81084b74] worker_thread+0x164/0x370 [81084a10] ? manage_workers.isra.30+0x130/0x130 [810893cc] kthread+0x8c/0xa0 [81660674] kernel_thread_helper+0x4/0x10 [81089340] ? flush_kthread_worker+0xa0/0xa0 [81660670] ? gs_change+0x13/0x13 Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [a03f5e1e] radeon_dp_detect+0x1de/0x230 [radeon] [a014b6b3] drm_helper_probe_single_connector_modes+0x2c3/0x380 [drm_kms_helper] [a0149d42] drm_fb_helper_hotplug_event+0xf2/0x150 [drm_kms_helper] [a03fe8d5] radeon_fb_output_poll_changed+0x15/0x20 [radeon] [a03f7b65] radeon_output_poll_changed+0x15/0x20 [radeon] [a014ad40] output_poll_execute+0x190/0x1a0 [drm_kms_helper] [a014abb0] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [81083dca] process_one_work+0x11a/0x480 [81084b74] worker_thread+0x164/0x370 [81084a10] ? manage_workers.isra.30+0x130/0x130 [810893cc] kthread+0x8c/0xa0 [81660674] kernel_thread_helper+0x4/0x10 [81089340] ? flush_kthread_worker+0xa0/0xa0 [81660670] ? gs_change+0x13/0x13 From these traces it looks like all that really needs to happen is to disable the output polling during suspend. The following patch seems to get rid of the problems I'm seeing. Does this look okay to you? From 0c01950f248c541198b7560793cf57c59b2c11f8 Mon Sep 17 00:00:00 2001 From: Seth Forshee seth.fors...@canonical.com Date: Tue, 31 Jan 2012 15:37:02 -0600 Subject: [PATCH 1/1] drm/radeon/kms: disable output polling when suspended Polling the outputs when the device is suspended can result in erroneous status updates. Disable output polling during suspend to prevent this from happening. Signed-off-by: Seth Forshee seth.fors...@canonical.com --- drivers/gpu/drm/radeon/radeon_device.c |4 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index cec51a5..49f7cb7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -883,6 +883,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev-switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, dev-mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -972,6 +974,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, dev-mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } -- 1.7.8.3 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 02:38:37PM -0500, Alex Deucher wrote: On Fri, Jan 20, 2012 at 10:53 AM, Seth Forshee seth.fors...@canonical.com wrote: On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: 2. Occasional long delays when suspending. When this happens I see messages like following in dmesg: [drm:atom_op_jump] *ERROR* atombios stuck in loop for more than 5secs aborting [drm:atom_execute_table_locked] *ERROR* atombios stuck executing D44E (len 62, WS 0, PS 0) @ 0xD46A Sometimes one of suspend or resume hangs completely, but I can't tell which and am not sure whether or not it's related. I'm also testing a Mac Mini with the exact same card which does not seem to suffer from this issue. I ran a bisections that identified f8d0edd (drm/radeon/kms: improve DP detect logic) as introducing problems with suspend, and reverting this patch on top of 3.2.1 does seem to eliminate both issues. That patch doesn't really affect the modesetting paths directly; it looks like a red herring to me. Perhaps. I just started a run of 200 s3 cycles with the patch reverted to see if I can reproduce the issues. I can usually trigger the problem with 15 or fewer s3 cycles. The machine completed 200 s3 cycles with the patch reverted without long delays, hangs, or any atombios error messages. Without reverting it doesn't get through many at all before experiencing the errors and long delays or hangs. I added a printout of the connector status resulting from the logic that was changed by f8d0edd. With the logic prior to this commit it consistently returns the status as disconnected, which is correct as I have nothing connected. But with the improved logic the status is sometimes reported as connected, and these coincide with the atombios errors. Do any of the disconnected displayport connectors get misdetected as connected during normal operation or does it only happen during suspend/resume? So far I've only seen them during suspend/resume. ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 04:39:31PM -0500, Alex Deucher wrote: On Fri, Jan 20, 2012 at 4:12 PM, Seth Forshee seth.fors...@canonical.com wrote: On Fri, Jan 20, 2012 at 02:38:37PM -0500, Alex Deucher wrote: On Fri, Jan 20, 2012 at 10:53 AM, Seth Forshee seth.fors...@canonical.com wrote: On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: 2. Occasional long delays when suspending. When this happens I see messages like following in dmesg: [drm:atom_op_jump] *ERROR* atombios stuck in loop for more than 5secs aborting [drm:atom_execute_table_locked] *ERROR* atombios stuck executing D44E (len 62, WS 0, PS 0) @ 0xD46A Sometimes one of suspend or resume hangs completely, but I can't tell which and am not sure whether or not it's related. I'm also testing a Mac Mini with the exact same card which does not seem to suffer from this issue. I ran a bisections that identified f8d0edd (drm/radeon/kms: improve DP detect logic) as introducing problems with suspend, and reverting this patch on top of 3.2.1 does seem to eliminate both issues. That patch doesn't really affect the modesetting paths directly; it looks like a red herring to me. Perhaps. I just started a run of 200 s3 cycles with the patch reverted to see if I can reproduce the issues. I can usually trigger the problem with 15 or fewer s3 cycles. The machine completed 200 s3 cycles with the patch reverted without long delays, hangs, or any atombios error messages. Without reverting it doesn't get through many at all before experiencing the errors and long delays or hangs. I added a printout of the connector status resulting from the logic that was changed by f8d0edd. With the logic prior to this commit it consistently returns the status as disconnected, which is correct as I have nothing connected. But with the improved logic the status is sometimes reported as connected, and these coincide with the atombios errors. Do any of the disconnected displayport connectors get misdetected as connected during normal operation or does it only happen during suspend/resume? So far I've only seen them during suspend/resume. Can you track down who is calling the connector-detect() callbacks during suspend and resume? I got two different stack traces, see below. And to slightly amend my statement above, I'm only seeing the wrong status returned on the suspend side of things. The status during resume always seems to be correct. Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [a03f5e1e] radeon_dp_detect+0x1de/0x230 [radeon] [a014ac6d] output_poll_execute+0xbd/0x1a0 [drm_kms_helper] [a014abb0] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [81083dca] process_one_work+0x11a/0x480 [81084b74] worker_thread+0x164/0x370 [81084a10] ? manage_workers.isra.30+0x130/0x130 [810893cc] kthread+0x8c/0xa0 [81660674] kernel_thread_helper+0x4/0x10 [81089340] ? flush_kthread_worker+0xa0/0xa0 [81660670] ? gs_change+0x13/0x13 Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [a03f5e1e] radeon_dp_detect+0x1de/0x230 [radeon] [a014b6b3] drm_helper_probe_single_connector_modes+0x2c3/0x380 [drm_kms_helper] [a0149d42] drm_fb_helper_hotplug_event+0xf2/0x150 [drm_kms_helper] [a03fe8d5] radeon_fb_output_poll_changed+0x15/0x20 [radeon] [a03f7b65] radeon_output_poll_changed+0x15/0x20 [radeon] [a014ad40] output_poll_execute+0x190/0x1a0 [drm_kms_helper] [a014abb0] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [81083dca] process_one_work+0x11a/0x480 [81084b74] worker_thread+0x164/0x370 [81084a10] ? manage_workers.isra.30+0x130/0x130 [810893cc] kthread+0x8c/0xa0 [81660674] kernel_thread_helper+0x4/0x10 [81089340] ? flush_kthread_worker+0xa0/0xa0 [81660670] ? gs_change+0x13/0x13 ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 11:09:29PM +0200, Pasi Kärkkäinen wrote: On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: On Thu, Jan 19, 2012 at 02:48:52PM -0500, Alex Deucher wrote: On Thu, Jan 19, 2012 at 12:18 PM, Seth Forshee seth.fors...@canonical.com wrote: I'm seeing several issues related to the radeon driver on a MacBook Pro 8,2 with the following graphics card: ATI Technologies Inc Whistler [AMD Radeon HD 6600M Series] [1002:6741] All problems were observed when using kernel version 3.2.1. None are seen when using fglrx. 1. Excessive power draw. When using the radeon driver ACPI reports a power draw of about 30W on an idle desktop. Using fglrx brings this number down to 15W. The power saving features of the open source driver are not yet as good as the closed source driver. Please see the power management section of this page (http://wiki.x.org/wiki/RadeonFeature) for more info on the options currently available. The dynpm option makes a small difference, saving about 2W. I did notice an ocassional flash on the screen with this option, and the same flash each time I changed the power options. Btw how do you measure the power draw? You can get the instantaneous rate from the data under /proc/acpi/battery, but I use a tool called powerstat [1], written by my colleague Colin King. The advantage of powerstat is that it samples the ACPI data over a period of time and reports the average and standard deviation. That way I have a better idea of how much power is really being drawn and the quality of the value reported. [1] http://kernel.ubuntu.com/git?p=cking/powerstat.git ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 11:09:29PM +0200, Pasi K?rkk?inen wrote: > On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: > > On Thu, Jan 19, 2012 at 02:48:52PM -0500, Alex Deucher wrote: > > > On Thu, Jan 19, 2012 at 12:18 PM, Seth Forshee > > > wrote: > > > > I'm seeing several issues related to the radeon driver on a MacBook Pro > > > > 8,2 with the following graphics card: > > > > > > > > ?ATI Technologies Inc Whistler [AMD Radeon HD 6600M Series] [1002:6741] > > > > > > > > All problems were observed when using kernel version 3.2.1. None are > > > > seen when using fglrx. > > > > > > > > ?1. Excessive power draw. When using the radeon driver ACPI reports a > > > > ? ?power draw of about 30W on an idle desktop. Using fglrx brings this > > > > ? ?number down to 15W. > > > > > > The power saving features of the open source driver are not yet as > > > good as the closed source driver. Please see the power management > > > section of this page (http://wiki.x.org/wiki/RadeonFeature) for more > > > info on the options currently available. > > > > The dynpm option makes a small difference, saving about 2W. I did notice > > an ocassional flash on the screen with this option, and the same flash > > each time I changed the power options. > > > > Btw how do you measure the power draw? You can get the instantaneous rate from the data under /proc/acpi/battery, but I use a tool called powerstat [1], written by my colleague Colin King. The advantage of powerstat is that it samples the ACPI data over a period of time and reports the average and standard deviation. That way I have a better idea of how much power is really being drawn and the quality of the value reported. [1] http://kernel.ubuntu.com/git?p=cking/powerstat.git
radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 04:39:31PM -0500, Alex Deucher wrote: > On Fri, Jan 20, 2012 at 4:12 PM, Seth Forshee > wrote: > > On Fri, Jan 20, 2012 at 02:38:37PM -0500, Alex Deucher wrote: > >> On Fri, Jan 20, 2012 at 10:53 AM, Seth Forshee > >> wrote: > >> > On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: > >> >> > > ?2. Occasional long delays when suspending. When this happens I see > >> >> > > ? ?messages like following in dmesg: > >> >> > > > >> >> > > ? ? ?[drm:atom_op_jump] *ERROR* atombios stuck in loop for more > >> >> > > than 5secs aborting > >> >> > > ? ? ?[drm:atom_execute_table_locked] *ERROR* atombios stuck > >> >> > > executing D44E (len 62, WS 0, PS 0) @ 0xD46A > >> >> > > > >> >> > > ? ?Sometimes one of suspend or resume hangs completely, but I can't > >> >> > > ? ?tell which and am not sure whether or not it's related. I'm also > >> >> > > ? ?testing a Mac Mini with the exact same card which does not seem > >> >> > > to > >> >> > > ? ?suffer from this issue. > >> >> > > > >> >> > > ? ?I ran a bisections that identified f8d0edd (drm/radeon/kms: > >> >> > > improve > >> >> > > ? ?DP detect logic) as introducing problems with suspend, and > >> >> > > reverting > >> >> > > ? ?this patch on top of 3.2.1 does seem to eliminate both issues. > >> >> > > > >> >> > > >> >> > That patch doesn't really affect the modesetting paths directly; it > >> >> > looks like a red herring to me. > >> >> > >> >> Perhaps. I just started a run of 200 s3 cycles with the patch reverted > >> >> to see if I can reproduce the issues. I can usually trigger the problem > >> >> with 15 or fewer s3 cycles. > >> > > >> > The machine completed 200 s3 cycles with the patch reverted without long > >> > delays, hangs, or any atombios error messages. Without reverting it > >> > doesn't get through many at all before experiencing the errors and long > >> > delays or hangs. > >> > > >> > I added a printout of the connector status resulting from the logic that > >> > was changed by f8d0edd. With the logic prior to this commit it > >> > consistently returns the status as disconnected, which is correct as I > >> > have nothing connected. But with the improved logic the status is > >> > sometimes reported as connected, and these coincide with the atombios > >> > errors. > >> > > >> > >> Do any of the disconnected displayport connectors get misdetected as > >> connected during normal operation or does it only happen during > >> suspend/resume? > > > > So far I've only seen them during suspend/resume. > > Can you track down who is calling the connector->detect() callbacks > during suspend and resume? I got two different stack traces, see below. And to slightly amend my statement above, I'm only seeing the wrong status returned on the suspend side of things. The status during resume always seems to be correct. Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [] radeon_dp_detect+0x1de/0x230 [radeon] [] output_poll_execute+0xbd/0x1a0 [drm_kms_helper] [] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [] process_one_work+0x11a/0x480 [] worker_thread+0x164/0x370 [] ? manage_workers.isra.30+0x130/0x130 [] kthread+0x8c/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? flush_kthread_worker+0xa0/0xa0 [] ? gs_change+0x13/0x13 Pid: 49, comm: kworker/0:2 Tainted: G C O 3.2.0-10-generic #17-Ubuntu Call Trace: [] radeon_dp_detect+0x1de/0x230 [radeon] [] drm_helper_probe_single_connector_modes+0x2c3/0x380 [drm_kms_helper] [] drm_fb_helper_hotplug_event+0xf2/0x150 [drm_kms_helper] [] radeon_fb_output_poll_changed+0x15/0x20 [radeon] [] radeon_output_poll_changed+0x15/0x20 [radeon] [] output_poll_execute+0x190/0x1a0 [drm_kms_helper] [] ? drm_helper_mode_fill_fb_struct+0x30/0x30 [drm_kms_helper] [] process_one_work+0x11a/0x480 [] worker_thread+0x164/0x370 [] ? manage_workers.isra.30+0x130/0x130 [] kthread+0x8c/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? flush_kthread_worker+0xa0/0xa0 [] ? gs_change+0x13/0x13
radeon issues on MacBook Pro 8,2
On Fri, Jan 20, 2012 at 02:38:37PM -0500, Alex Deucher wrote: > On Fri, Jan 20, 2012 at 10:53 AM, Seth Forshee > wrote: > > On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: > >> > > ?2. Occasional long delays when suspending. When this happens I see > >> > > ? ?messages like following in dmesg: > >> > > > >> > > ? ? ?[drm:atom_op_jump] *ERROR* atombios stuck in loop for more than > >> > > 5secs aborting > >> > > ? ? ?[drm:atom_execute_table_locked] *ERROR* atombios stuck executing > >> > > D44E (len 62, WS 0, PS 0) @ 0xD46A > >> > > > >> > > ? ?Sometimes one of suspend or resume hangs completely, but I can't > >> > > ? ?tell which and am not sure whether or not it's related. I'm also > >> > > ? ?testing a Mac Mini with the exact same card which does not seem to > >> > > ? ?suffer from this issue. > >> > > > >> > > ? ?I ran a bisections that identified f8d0edd (drm/radeon/kms: improve > >> > > ? ?DP detect logic) as introducing problems with suspend, and reverting > >> > > ? ?this patch on top of 3.2.1 does seem to eliminate both issues. > >> > > > >> > > >> > That patch doesn't really affect the modesetting paths directly; it > >> > looks like a red herring to me. > >> > >> Perhaps. I just started a run of 200 s3 cycles with the patch reverted > >> to see if I can reproduce the issues. I can usually trigger the problem > >> with 15 or fewer s3 cycles. > > > > The machine completed 200 s3 cycles with the patch reverted without long > > delays, hangs, or any atombios error messages. Without reverting it > > doesn't get through many at all before experiencing the errors and long > > delays or hangs. > > > > I added a printout of the connector status resulting from the logic that > > was changed by f8d0edd. With the logic prior to this commit it > > consistently returns the status as disconnected, which is correct as I > > have nothing connected. But with the improved logic the status is > > sometimes reported as connected, and these coincide with the atombios > > errors. > > > > Do any of the disconnected displayport connectors get misdetected as > connected during normal operation or does it only happen during > suspend/resume? So far I've only seen them during suspend/resume.
radeon issues on MacBook Pro 8,2
On Thu, Jan 19, 2012 at 02:53:17PM -0600, Seth Forshee wrote: > > > ?2. Occasional long delays when suspending. When this happens I see > > > ? ?messages like following in dmesg: > > > > > > ? ? ?[drm:atom_op_jump] *ERROR* atombios stuck in loop for more than > > > 5secs aborting > > > ? ? ?[drm:atom_execute_table_locked] *ERROR* atombios stuck executing > > > D44E (len 62, WS 0, PS 0) @ 0xD46A > > > > > > ? ?Sometimes one of suspend or resume hangs completely, but I can't > > > ? ?tell which and am not sure whether or not it's related. I'm also > > > ? ?testing a Mac Mini with the exact same card which does not seem to > > > ? ?suffer from this issue. > > > > > > ? ?I ran a bisections that identified f8d0edd (drm/radeon/kms: improve > > > ? ?DP detect logic) as introducing problems with suspend, and reverting > > > ? ?this patch on top of 3.2.1 does seem to eliminate both issues. > > > > > > > That patch doesn't really affect the modesetting paths directly; it > > looks like a red herring to me. > > Perhaps. I just started a run of 200 s3 cycles with the patch reverted > to see if I can reproduce the issues. I can usually trigger the problem > with 15 or fewer s3 cycles. The machine completed 200 s3 cycles with the patch reverted without long delays, hangs, or any atombios error messages. Without reverting it doesn't get through many at all before experiencing the errors and long delays or hangs. I added a printout of the connector status resulting from the logic that was changed by f8d0edd. With the logic prior to this commit it consistently returns the status as disconnected, which is correct as I have nothing connected. But with the improved logic the status is sometimes reported as connected, and these coincide with the atombios errors. Seth
radeon issues on MacBook Pro 8,2
On Thu, Jan 19, 2012 at 02:48:52PM -0500, Alex Deucher wrote: > On Thu, Jan 19, 2012 at 12:18 PM, Seth Forshee > wrote: > > I'm seeing several issues related to the radeon driver on a MacBook Pro > > 8,2 with the following graphics card: > > > > ?ATI Technologies Inc Whistler [AMD Radeon HD 6600M Series] [1002:6741] > > > > All problems were observed when using kernel version 3.2.1. None are > > seen when using fglrx. > > > > ?1. Excessive power draw. When using the radeon driver ACPI reports a > > ? ?power draw of about 30W on an idle desktop. Using fglrx brings this > > ? ?number down to 15W. > > The power saving features of the open source driver are not yet as > good as the closed source driver. Please see the power management > section of this page (http://wiki.x.org/wiki/RadeonFeature) for more > info on the options currently available. The dynpm option makes a small difference, saving about 2W. I did notice an ocassional flash on the screen with this option, and the same flash each time I changed the power options. The only thing that improves things significatnly is the "low" profile method, which gets it down to about 18.5W. > > ?2. Occasional long delays when suspending. When this happens I see > > ? ?messages like following in dmesg: > > > > ? ? ?[drm:atom_op_jump] *ERROR* atombios stuck in loop for more than 5secs > > aborting > > ? ? ?[drm:atom_execute_table_locked] *ERROR* atombios stuck executing D44E > > (len 62, WS 0, PS 0) @ 0xD46A > > > > ? ?Sometimes one of suspend or resume hangs completely, but I can't > > ? ?tell which and am not sure whether or not it's related. I'm also > > ? ?testing a Mac Mini with the exact same card which does not seem to > > ? ?suffer from this issue. > > > > ? ?I ran a bisections that identified f8d0edd (drm/radeon/kms: improve > > ? ?DP detect logic) as introducing problems with suspend, and reverting > > ? ?this patch on top of 3.2.1 does seem to eliminate both issues. > > > > That patch doesn't really affect the modesetting paths directly; it > looks like a red herring to me. Perhaps. I just started a run of 200 s3 cycles with the patch reverted to see if I can reproduce the issues. I can usually trigger the problem with 15 or fewer s3 cycles. > > ?3. When the LVDS panel is powered off and back on, the display > > ? ?flickers, as if the backlight is cycling rapidly between low and > > ? ?high brightness. If the panel is left on this effect gradually > > ? ?lessens and is eventually no longer noticable. This is not seen with > > ? ?fglrx. > > > > For the sake of tracking this properly, it would probably be best to file a > bug: > https://bugs.freedesktop.org > Product: DRI > Component: DRM/Radeon > What connectors does your card actually have on it? Please attach a > copy of your dmesg output and vbios: > (as root) > (use lspci to get the bus id) > cd /sys/bus/pci/devices/ > echo 1 > rom > cat rom > /tmp/vbios.rom > echo 0 > rom > > Also, keep in mind that this is a mac so it's likely there may be > wonky mac specific problems. I filed bug #44955 for the flickering issue. Thanks, Seth
radeon issues on MacBook Pro 8,2
I'm seeing several issues related to the radeon driver on a MacBook Pro 8,2 with the following graphics card: ATI Technologies Inc Whistler [AMD Radeon HD 6600M Series] [1002:6741] All problems were observed when using kernel version 3.2.1. None are seen when using fglrx. 1. Excessive power draw. When using the radeon driver ACPI reports a power draw of about 30W on an idle desktop. Using fglrx brings this number down to 15W. 2. Occasional long delays when suspending. When this happens I see messages like following in dmesg: [drm:atom_op_jump] *ERROR* atombios stuck in loop for more than 5secs aborting [drm:atom_execute_table_locked] *ERROR* atombios stuck executing D44E (len 62, WS 0, PS 0) @ 0xD46A Sometimes one of suspend or resume hangs completely, but I can't tell which and am not sure whether or not it's related. I'm also testing a Mac Mini with the exact same card which does not seem to suffer from this issue. I ran a bisections that identified f8d0edd (drm/radeon/kms: improve DP detect logic) as introducing problems with suspend, and reverting this patch on top of 3.2.1 does seem to eliminate both issues. 3. When the LVDS panel is powered off and back on, the display flickers, as if the backlight is cycling rapidly between low and high brightness. If the panel is left on this effect gradually lessens and is eventually no longer noticable. This is not seen with fglrx. Thanks, Seth
Re: radeon issues on MacBook Pro 8,2
On Thu, Jan 19, 2012 at 02:48:52PM -0500, Alex Deucher wrote: On Thu, Jan 19, 2012 at 12:18 PM, Seth Forshee seth.fors...@canonical.com wrote: I'm seeing several issues related to the radeon driver on a MacBook Pro 8,2 with the following graphics card: ATI Technologies Inc Whistler [AMD Radeon HD 6600M Series] [1002:6741] All problems were observed when using kernel version 3.2.1. None are seen when using fglrx. 1. Excessive power draw. When using the radeon driver ACPI reports a power draw of about 30W on an idle desktop. Using fglrx brings this number down to 15W. The power saving features of the open source driver are not yet as good as the closed source driver. Please see the power management section of this page (http://wiki.x.org/wiki/RadeonFeature) for more info on the options currently available. The dynpm option makes a small difference, saving about 2W. I did notice an ocassional flash on the screen with this option, and the same flash each time I changed the power options. The only thing that improves things significatnly is the low profile method, which gets it down to about 18.5W. 2. Occasional long delays when suspending. When this happens I see messages like following in dmesg: [drm:atom_op_jump] *ERROR* atombios stuck in loop for more than 5secs aborting [drm:atom_execute_table_locked] *ERROR* atombios stuck executing D44E (len 62, WS 0, PS 0) @ 0xD46A Sometimes one of suspend or resume hangs completely, but I can't tell which and am not sure whether or not it's related. I'm also testing a Mac Mini with the exact same card which does not seem to suffer from this issue. I ran a bisections that identified f8d0edd (drm/radeon/kms: improve DP detect logic) as introducing problems with suspend, and reverting this patch on top of 3.2.1 does seem to eliminate both issues. That patch doesn't really affect the modesetting paths directly; it looks like a red herring to me. Perhaps. I just started a run of 200 s3 cycles with the patch reverted to see if I can reproduce the issues. I can usually trigger the problem with 15 or fewer s3 cycles. 3. When the LVDS panel is powered off and back on, the display flickers, as if the backlight is cycling rapidly between low and high brightness. If the panel is left on this effect gradually lessens and is eventually no longer noticable. This is not seen with fglrx. For the sake of tracking this properly, it would probably be best to file a bug: https://bugs.freedesktop.org Product: DRI Component: DRM/Radeon What connectors does your card actually have on it? Please attach a copy of your dmesg output and vbios: (as root) (use lspci to get the bus id) cd /sys/bus/pci/devices/pci bus id echo 1 rom cat rom /tmp/vbios.rom echo 0 rom Also, keep in mind that this is a mac so it's likely there may be wonky mac specific problems. I filed bug #44955 for the flickering issue. Thanks, Seth ___ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel