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…
Add table
Reference in a new issue