aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2016-11-09 12:17:44 -0500
committerBen Skeggs <bskeggs@redhat.com>2016-11-16 18:50:37 -0500
commit3a6536c51d5db3adf58dcd466a3aee6233b58544 (patch)
tree1ed563a1d9eeeac4a70c3206d3cb5a19fd11bd99
parentdc2b65592801b80fe8944cb84f635e1725a7bd98 (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.c59
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h6
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
363static void
364nouveau_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
376static int
377nouveau_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
351int 401int
352nouveau_display_init(struct drm_device *dev) 402nouveau_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
537vblank_err: 593vblank_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;