From patchwork Fri Sep 26 22:57:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Demharter X-Patchwork-Id: 4988061 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 636EEBEEA6 for ; Fri, 26 Sep 2014 22:57:39 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 31FA420222 for ; Fri, 26 Sep 2014 22:57:38 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id E91A22021B for ; Fri, 26 Sep 2014 22:57:36 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B3F7A6E08E; Fri, 26 Sep 2014 15:57:35 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mout.gmx.net (mout.gmx.net [212.227.15.18]) by gabe.freedesktop.org (Postfix) with ESMTP id DC9416E08E for ; Fri, 26 Sep 2014 15:57:33 -0700 (PDT) Received: from [192.168.1.132] ([178.196.108.113]) by mail.gmx.com (mrgmx002) with ESMTPSA (Nemesis) id 0LcT2M-1XxK2E2LI6-00jrHp for ; Sat, 27 Sep 2014 00:57:24 +0200 Message-ID: <5425EF41.3000104@gmx.net> Date: Sat, 27 Sep 2014 00:57:05 +0200 From: Stefan Demharter User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.1.2 MIME-Version: 1.0 To: dri-devel@lists.freedesktop.org Subject: [PATCH RFC v3] vga_switcheroo: restore mux and power state upon resume from hibernation X-Provags-ID: V03:K0:dVZmef0iL4MyXi800QhDKW+sy2LY5hGd5P2K5abHIKjhFwg1eOG m4llhID6d6xmjsRCevTWahTLCCqhrJjAJLT03rrzX6m/izxae3IcRdn4Ki4NgZ8u8VqZ7zl Iq0Au7Cb0eGLtJyY9RAdQZIRtdW3QtC0l3+KF7Vmz+/uKBO78iM+3Fal7bJ3v75oHJBuvOg WycT7vvGPFZ5ULg7/Si2w== X-UI-Out-Filterresults: notjunk:1; X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Restore state of mux switch and power-off state of graphics cards upon resume from hibernation as the following problems can occur: If the mux is set to the discrete card prior to hibernation it is reset to the integrated card by the system upon boot. Thus it stays at the wrong state after resume which results in a black screen. Solve this by saving the current state of the mux and restoring it upon resume from hibernation. If a graphics card is powered off prior to hibernation it is powered up by the system upon boot. Thus it stays still active after resume and there is an inconsistence between the power state the kernel thinks the graphic card is in and the actual power state. In case of the discrete card this also results in a noticeable increase in power consumption. Solve this by powering off such cards upon resume. Signed-off-by: Stefan Demharter --- drivers/gpu/vga/vga_switcheroo.c | 123 +++++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 37ac7b5..67a82f1 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -72,6 +73,89 @@ static struct vgasr_priv vgasr_priv = { .clients = LIST_HEAD_INIT(vgasr_priv.clients), }; +static int vga_switchoff(struct vga_switcheroo_client *client); + +/* Assume that the initial state of the mux is set to IGD at boot time */ +#define MUX_INITIAL_STATE VGA_SWITCHEROO_IGD + +/* + * The next variable stores the state of the mux + * so that it can be restored upon resume from hibernation. + * + * This should also handle muxless systems quite well + * since there mux_state is never altered and the function + * restore_mux will end up doing nothing + */ +static int mux_state = MUX_INITIAL_STATE; + +#define DIS_IGD_STR(id) ((id) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD") + +static int vga_switchon(struct vga_switcheroo_client *client); +static int vga_switchoff(struct vga_switcheroo_client *client); + +/* Restore saved mux state */ +static void vga_restore_mux(void) +{ + if (vgasr_priv.handler->switchto && + mux_state != MUX_INITIAL_STATE) { + int ret; + + pr_info("vga_switcheroo: restoring mux state to %s\n", + DIS_IGD_STR(mux_state)); + ret = vgasr_priv.handler->switchto(mux_state); + + if (ret) + pr_warn("vga_switcheroo: failed (%d)\n", ret); + } +} + +/* + * Power off devices from which the system thinks they are powered-off. + * If a device is powered-off before hibernation the system may + * power it on upon boot. This results in a mismatch between what + * the driver thinks of the power state and the actual power state. + * This function resolves this mismatch by powering-off corresponding devices. + */ +static void vga_restore_power_off_state(void) +{ + struct vga_switcheroo_client *client; + + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (client_is_audio(client)) + continue; + + if (client->pwr_state == VGA_SWITCHEROO_OFF) { + pr_info("vga_switcheroo: Restoring power-off state for %s\n", + DIS_IGD_STR(client->id)); + vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); + } + } +} + +/* + * Callback to handle resume from hibernation. + * Restore mux state and power states for switcheroo controlled GPUs. + */ +static int vga_switcheroo_resume(struct notifier_block *nb, + unsigned long action, void *unused) +{ + if (action == PM_POST_HIBERNATION) { + mutex_lock(&vgasr_mutex); + + vga_restore_mux(); + vga_restore_power_off_state(); + + mutex_unlock(&vgasr_mutex); + } + + return 0; +} + +static struct notifier_block vga_pm_nb = { + .notifier_call = vga_switcheroo_resume, + .priority = 0, +}; + static bool vga_switcheroo_ready(void) { /* we're ready if we get two clients + handler */ @@ -83,6 +167,7 @@ static void vga_switcheroo_enable(void) { int ret; struct vga_switcheroo_client *client; + register_pm_notifier(&vga_pm_nb); /* call the handler to init */ if (vgasr_priv.handler->init) @@ -119,14 +204,20 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) } EXPORT_SYMBOL(vga_switcheroo_register_handler); +void vga_switcheroo_disable(void) +{ + unregister_pm_notifier(&vga_pm_nb); + pr_info("vga_switcheroo: disabled\n"); + vga_switcheroo_debugfs_fini(&vgasr_priv); + vgasr_priv.active = false; +} + 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; + vga_switcheroo_disable(); } mutex_unlock(&vgasr_mutex); } @@ -235,9 +326,7 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev) kfree(client); } if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { - printk(KERN_INFO "vga_switcheroo: disabled\n"); - vga_switcheroo_debugfs_fini(&vgasr_priv); - vgasr_priv.active = false; + vga_switcheroo_disable(); } mutex_unlock(&vgasr_mutex); } @@ -262,12 +351,13 @@ static int vga_switcheroo_show(struct seq_file *m, void *v) int i = 0; mutex_lock(&vgasr_mutex); list_for_each_entry(client, &vgasr_priv.clients, list) { - seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, - client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", + seq_printf(m, "%d:%s%s:%c:%s%s:%s:%s\n", i, + DIS_IGD_STR(client_id(client)), client_is_vga(client) ? "" : "-Audio", client->active ? '+' : ' ', client->driver_power_control ? "Dyn" : "", client->pwr_state ? "Pwr" : "Off", + client_id(client) == mux_state ? "mux" : "", pci_name(client->pdev)); i++; } @@ -304,6 +394,16 @@ static int vga_switchoff(struct vga_switcheroo_client *client) return 0; } +static int vga_switch_mux_to(int client_id) +{ + int ret = vgasr_priv.handler->switchto(client_id); + + if (ret == 0) + mux_state = client_id; + + return ret; +} + static void set_audio_state(int id, int state) { struct vga_switcheroo_client *client; @@ -353,7 +453,8 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) console_unlock(); } - ret = vgasr_priv.handler->switchto(new_client->id); + ret = vga_switch_mux_to(new_client->id); + if (ret) return ret; @@ -468,7 +569,7 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, vgasr_priv.delayed_switch_active = false; if (just_mux) { - ret = vgasr_priv.handler->switchto(client_id); + ret = vga_switch_mux_to(client_id); goto out; } @@ -624,7 +725,7 @@ static int vga_switcheroo_runtime_suspend(struct device *dev) if (ret) return ret; if (vgasr_priv.handler->switchto) - vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD); + vga_switch_mux_to(VGA_SWITCHEROO_IGD); vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF); return 0; }