diff options
author | Hans de Goede <hdegoede@redhat.com> | 2016-11-09 12:17:44 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2016-11-16 18:50:37 -0500 |
commit | 3a6536c51d5db3adf58dcd466a3aee6233b58544 (patch) | |
tree | 1ed563a1d9eeeac4a70c3206d3cb5a19fd11bd99 | |
parent | dc2b65592801b80fe8944cb84f635e1725a7bd98 (diff) |
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>
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 6 |
2 files changed, 65 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 75c90a8da18a..009a6f53d55a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -24,6 +24,7 @@ | |||
24 | * | 24 | * |
25 | */ | 25 | */ |
26 | 26 | ||
27 | #include <acpi/video.h> | ||
27 | #include <drm/drmP.h> | 28 | #include <drm/drmP.h> |
28 | #include <drm/drm_atomic.h> | 29 | #include <drm/drm_atomic.h> |
29 | #include <drm/drm_atomic_helper.h> | 30 | #include <drm/drm_atomic_helper.h> |
@@ -348,6 +349,55 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = { | |||
348 | } \ | 349 | } \ |
349 | } while(0) | 350 | } while(0) |
350 | 351 | ||
352 | #ifdef CONFIG_ACPI | ||
353 | |||
354 | /* | ||
355 | * Hans de Goede: This define belongs in acpi/video.h, I've submitted a patch | ||
356 | * to the acpi subsys to move it there from drivers/acpi/acpi_video.c . | ||
357 | * This should be dropped once that is merged. | ||
358 | */ | ||
359 | #ifndef ACPI_VIDEO_NOTIFY_PROBE | ||
360 | #define ACPI_VIDEO_NOTIFY_PROBE 0x81 | ||
361 | #endif | ||
362 | |||
363 | static void | ||
364 | nouveau_display_acpi_work(struct work_struct *work) | ||
365 | { | ||
366 | struct nouveau_drm *drm = container_of(work, typeof(*drm), acpi_work); | ||
367 | |||
368 | pm_runtime_get_sync(drm->dev->dev); | ||
369 | |||
370 | drm_helper_hpd_irq_event(drm->dev); | ||
371 | |||
372 | pm_runtime_mark_last_busy(drm->dev->dev); | ||
373 | pm_runtime_put_sync(drm->dev->dev); | ||
374 | } | ||
375 | |||
376 | static int | ||
377 | nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val, | ||
378 | void *data) | ||
379 | { | ||
380 | struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); | ||
381 | struct acpi_bus_event *info = data; | ||
382 | |||
383 | if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { | ||
384 | if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { | ||
385 | /* | ||
386 | * This may be the only indication we receive of a | ||
387 | * connector hotplug on a runtime suspended GPU, | ||
388 | * schedule acpi_work to check. | ||
389 | */ | ||
390 | schedule_work(&drm->acpi_work); | ||
391 | |||
392 | /* acpi-video should not generate keypresses for this */ | ||
393 | return NOTIFY_BAD; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | return NOTIFY_DONE; | ||
398 | } | ||
399 | #endif | ||
400 | |||
351 | int | 401 | int |
352 | nouveau_display_init(struct drm_device *dev) | 402 | nouveau_display_init(struct drm_device *dev) |
353 | { | 403 | { |
@@ -532,6 +582,12 @@ nouveau_display_create(struct drm_device *dev) | |||
532 | } | 582 | } |
533 | 583 | ||
534 | nouveau_backlight_init(dev); | 584 | nouveau_backlight_init(dev); |
585 | #ifdef CONFIG_ACPI | ||
586 | INIT_WORK(&drm->acpi_work, nouveau_display_acpi_work); | ||
587 | drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy; | ||
588 | register_acpi_notifier(&drm->acpi_nb); | ||
589 | #endif | ||
590 | |||
535 | return 0; | 591 | return 0; |
536 | 592 | ||
537 | vblank_err: | 593 | vblank_err: |
@@ -547,6 +603,9 @@ nouveau_display_destroy(struct drm_device *dev) | |||
547 | { | 603 | { |
548 | struct nouveau_display *disp = nouveau_display(dev); | 604 | struct nouveau_display *disp = nouveau_display(dev); |
549 | 605 | ||
606 | #ifdef CONFIG_ACPI | ||
607 | unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); | ||
608 | #endif | ||
550 | nouveau_backlight_exit(dev); | 609 | nouveau_backlight_exit(dev); |
551 | nouveau_display_vblank_fini(dev); | 610 | nouveau_display_vblank_fini(dev); |
552 | 611 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 4cd47bae73c7..ae1fd641c96e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -37,6 +37,8 @@ | |||
37 | * - implemented limited ABI16/NVIF interop | 37 | * - implemented limited ABI16/NVIF interop |
38 | */ | 38 | */ |
39 | 39 | ||
40 | #include <linux/notifier.h> | ||
41 | |||
40 | #include <nvif/client.h> | 42 | #include <nvif/client.h> |
41 | #include <nvif/device.h> | 43 | #include <nvif/device.h> |
42 | #include <nvif/ioctl.h> | 44 | #include <nvif/ioctl.h> |
@@ -161,6 +163,10 @@ struct nouveau_drm { | |||
161 | struct nvbios vbios; | 163 | struct nvbios vbios; |
162 | struct nouveau_display *display; | 164 | struct nouveau_display *display; |
163 | struct backlight_device *backlight; | 165 | struct backlight_device *backlight; |
166 | #ifdef CONFIG_ACPI | ||
167 | struct notifier_block acpi_nb; | ||
168 | struct work_struct acpi_work; | ||
169 | #endif | ||
164 | 170 | ||
165 | /* power management */ | 171 | /* power management */ |
166 | struct nouveau_hwmon *hwmon; | 172 | struct nouveau_hwmon *hwmon; |