forked from mirrors/linux
		
	drm/nouveau: Intercept ACPI_VIDEO_NOTIFY_PROBE
Various notebooks with nvidia GPUs generate an ACPI_VIDEO_NOTIFY_PROBE acpi-video event when an external device gets plugged in (and again on modesets on that connector), the default behavior in the acpi-video driver for this is to send a KEY_SWITCHVIDEOMODE evdev event, which causes e.g. gnome-settings-daemon to ask us to rescan the connectors (good), but also causes g-s-d to switch to mirror mode on a newly plugged monitor rather then using the monitor to extend the desktop (bad) as KEY_SWITCHVIDEOMODE is supposed to switch between extend the desktop vs mirror mode. More troublesome are the repeated ACPI_VIDEO_NOTIFY_PROBE events on changing the mode on the connector, which cause g-s-d to switch between mirror/extend mode, which causes a new ACPI_VIDEO_NOTIFY_PROBE event and we end up with an endless loop. This commit fixes this by adding an acpi notifier block handler to nouveau_display.c to intercept ACPI_VIDEO_NOTIFY_PROBE and: 1) Wake-up runtime suspended GPUs and call drm_helper_hpd_irq_event() on them, this is necessary in some cases for the GPU to detect connector hotplug events while runtime suspended 2) Return NOTIFY_BAD to stop acpi-video from emitting a bogus KEY_SWITCHVIDEOMODE key-press event There already is another acpi notifier block handler registered in drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c, but that is not suitable since that one gets unregistered on runtime suspend, and we also want to intercept ACPI_VIDEO_NOTIFY_PROBE when runtime suspended. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
		
							parent
							
								
									dc2b655928
								
							
						
					
					
						commit
						3a6536c51d
					
				
					 2 changed files with 65 additions and 0 deletions
				
			
		|  | @ -24,6 +24,7 @@ | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <acpi/video.h> | ||||||
| #include <drm/drmP.h> | #include <drm/drmP.h> | ||||||
| #include <drm/drm_atomic.h> | #include <drm/drm_atomic.h> | ||||||
| #include <drm/drm_atomic_helper.h> | #include <drm/drm_atomic_helper.h> | ||||||
|  | @ -348,6 +349,55 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = { | ||||||
| 	}                                                                      \ | 	}                                                                      \ | ||||||
| } while(0) | } while(0) | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_ACPI | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Hans de Goede: This define belongs in acpi/video.h, I've submitted a patch | ||||||
|  |  * to the acpi subsys to move it there from drivers/acpi/acpi_video.c . | ||||||
|  |  * This should be dropped once that is merged. | ||||||
|  |  */ | ||||||
|  | #ifndef ACPI_VIDEO_NOTIFY_PROBE | ||||||
|  | #define ACPI_VIDEO_NOTIFY_PROBE			0x81 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | nouveau_display_acpi_work(struct work_struct *work) | ||||||
|  | { | ||||||
|  | 	struct nouveau_drm *drm = container_of(work, typeof(*drm), acpi_work); | ||||||
|  | 
 | ||||||
|  | 	pm_runtime_get_sync(drm->dev->dev); | ||||||
|  | 
 | ||||||
|  | 	drm_helper_hpd_irq_event(drm->dev); | ||||||
|  | 
 | ||||||
|  | 	pm_runtime_mark_last_busy(drm->dev->dev); | ||||||
|  | 	pm_runtime_put_sync(drm->dev->dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val, | ||||||
|  | 			  void *data) | ||||||
|  | { | ||||||
|  | 	struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); | ||||||
|  | 	struct acpi_bus_event *info = data; | ||||||
|  | 
 | ||||||
|  | 	if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { | ||||||
|  | 		if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * This may be the only indication we receive of a | ||||||
|  | 			 * connector hotplug on a runtime suspended GPU, | ||||||
|  | 			 * schedule acpi_work to check. | ||||||
|  | 			 */ | ||||||
|  | 			schedule_work(&drm->acpi_work); | ||||||
|  | 
 | ||||||
|  | 			/* acpi-video should not generate keypresses for this */ | ||||||
|  | 			return NOTIFY_BAD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NOTIFY_DONE; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| int | int | ||||||
| nouveau_display_init(struct drm_device *dev) | nouveau_display_init(struct drm_device *dev) | ||||||
| { | { | ||||||
|  | @ -532,6 +582,12 @@ nouveau_display_create(struct drm_device *dev) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nouveau_backlight_init(dev); | 	nouveau_backlight_init(dev); | ||||||
|  | #ifdef CONFIG_ACPI | ||||||
|  | 	INIT_WORK(&drm->acpi_work, nouveau_display_acpi_work); | ||||||
|  | 	drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy; | ||||||
|  | 	register_acpi_notifier(&drm->acpi_nb); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| vblank_err: | vblank_err: | ||||||
|  | @ -547,6 +603,9 @@ nouveau_display_destroy(struct drm_device *dev) | ||||||
| { | { | ||||||
| 	struct nouveau_display *disp = nouveau_display(dev); | 	struct nouveau_display *disp = nouveau_display(dev); | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_ACPI | ||||||
|  | 	unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); | ||||||
|  | #endif | ||||||
| 	nouveau_backlight_exit(dev); | 	nouveau_backlight_exit(dev); | ||||||
| 	nouveau_display_vblank_fini(dev); | 	nouveau_display_vblank_fini(dev); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,8 @@ | ||||||
|  *      - implemented limited ABI16/NVIF interop |  *      - implemented limited ABI16/NVIF interop | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <linux/notifier.h> | ||||||
|  | 
 | ||||||
| #include <nvif/client.h> | #include <nvif/client.h> | ||||||
| #include <nvif/device.h> | #include <nvif/device.h> | ||||||
| #include <nvif/ioctl.h> | #include <nvif/ioctl.h> | ||||||
|  | @ -161,6 +163,10 @@ struct nouveau_drm { | ||||||
| 	struct nvbios vbios; | 	struct nvbios vbios; | ||||||
| 	struct nouveau_display *display; | 	struct nouveau_display *display; | ||||||
| 	struct backlight_device *backlight; | 	struct backlight_device *backlight; | ||||||
|  | #ifdef CONFIG_ACPI | ||||||
|  | 	struct notifier_block acpi_nb; | ||||||
|  | 	struct work_struct acpi_work; | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 	/* power management */ | 	/* power management */ | ||||||
| 	struct nouveau_hwmon *hwmon; | 	struct nouveau_hwmon *hwmon; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Hans de Goede
						Hans de Goede