From patchwork Tue Sep 11 06:59:01 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Airlie X-Patchwork-Id: 1436201 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork2.kernel.org (Postfix) with ESMTP id 90EDCDFAF3 for ; Tue, 11 Sep 2012 09:33:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 917A39ECC0 for ; Tue, 11 Sep 2012 02:33:46 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by gabe.freedesktop.org (Postfix) with ESMTP id 7D90A9E7BC for ; Mon, 10 Sep 2012 23:59:07 -0700 (PDT) Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q8B6x7ZU012534 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 11 Sep 2012 02:59:07 -0400 Received: from prime.bne.redhat.com (dhcp-40-183.bne.redhat.com [10.64.40.183]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q8B6x3kK004771 for ; Tue, 11 Sep 2012 02:59:06 -0400 From: Dave Airlie To: dri-devel@lists.freedesktop.org Subject: [PATCH 2/2] nouveau: add runtime PM support (v0.1) Date: Tue, 11 Sep 2012 16:59:01 +1000 Message-Id: <1347346741-3020-3-git-send-email-airlied@gmail.com> In-Reply-To: <1347346741-3020-1-git-send-email-airlied@gmail.com> References: <1347346741-3020-1-git-send-email-airlied@gmail.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org From: Dave Airlie This adds hook into the system runtime PM support to enable dynamic PM on the nvidia device. It doesn't hook into the power switch yet, only D3s the card. TODO: keep a reference if we have enabled outputs/framebuffers (??) keep a reference if we have any requests in flight. sprinkle mark last busy for autosuspend timeout Signed-off-by: Dave Airlie - +#include #include "drmP.h" #include "drm_edid.h" #include "drm_crtc_helper.h" @@ -239,6 +239,12 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) struct nouveau_encoder *nv_partner; struct nouveau_i2c_port *i2c; int type; + int ret; + enum drm_connector_status conn_status = connector_status_disconnected; + + ret = pm_runtime_get_sync(connector->dev->dev); + if (ret < 0) + return ret; /* Cleanup the previous EDID block. */ if (nv_connector->edid) { @@ -262,7 +268,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { NV_ERROR(drm, "Detected %s, but failed init\n", drm_get_connector_name(connector)); - return connector_status_disconnected; + conn_status = connector_status_disconnected; + goto out; } /* Override encoder type for DVI-I based on whether EDID @@ -289,13 +296,15 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) } nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } nv_encoder = nouveau_connector_of_detect(connector); if (nv_encoder) { nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } detect_analog: @@ -310,12 +319,18 @@ detect_analog: if (helper->detect(encoder, connector) == connector_status_connected) { nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } } - return connector_status_disconnected; + out: + + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + + return conn_status; } static enum drm_connector_status diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index bb51a00..63b1b7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -25,7 +25,10 @@ #include #include #include - +#include +#include +#include "drmP.h" +#include "drm_crtc_helper.h" #include #include #include @@ -334,6 +337,13 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) nouveau_accel_init(drm); nouveau_fbcon_init(dev); + + pm_runtime_use_autosuspend(dev->dev); + pm_runtime_set_autosuspend_delay(dev->dev, 5000); + pm_runtime_set_active(dev->dev); + pm_runtime_allow(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + pm_runtime_mark_last_busy(dev->dev); return 0; fail_dispinit: @@ -349,6 +359,7 @@ fail_ttm: nouveau_vga_fini(drm); fail_device: nouveau_cli_destroy(&drm->client); + return ret; } @@ -357,6 +368,7 @@ nouveau_drm_unload(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); + pm_runtime_get_noresume(dev->dev); nouveau_fbcon_fini(dev); nouveau_accel_fini(drm); @@ -523,16 +535,21 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) struct nouveau_cli *cli; int ret; + /* need to bring up power immediately if opening device */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + return ret; + ret = nouveau_cli_create(pdev, fpriv->pid, sizeof(*cli), (void **)&cli); if (ret) - return ret; + goto out_suspend; if (nv_device(drm->device)->card_type >= NV_50) { ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40), 0x1000, &cli->base.vm); if (ret) { nouveau_cli_destroy(cli); - return ret; + goto out_suspend; } } @@ -541,7 +558,12 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) mutex_lock(&drm->client.mutex); list_add(&cli->head, &drm->clients); mutex_unlock(&drm->client.mutex); - return 0; + +out_suspend: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; } static void @@ -550,12 +572,15 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv) struct nouveau_cli *cli = nouveau_cli(fpriv); struct nouveau_drm *drm = nouveau_drm(dev); + pm_runtime_get_sync(dev->dev); + if (cli->abi16) nouveau_abi16_fini(cli->abi16); mutex_lock(&drm->client.mutex); list_del(&cli->head); mutex_unlock(&drm->client.mutex); + } static void @@ -563,6 +588,8 @@ nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) { struct nouveau_cli *cli = nouveau_cli(fpriv); nouveau_cli_destroy(cli); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); } static struct drm_ioctl_desc @@ -581,12 +608,29 @@ nouveau_ioctls[] = { DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH), }; +long nouveau_drm_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev; + long ret; + dev = file_priv->minor->dev; + ret = pm_runtime_get(dev->dev); + if (ret < 0) + return ret; + + ret = drm_ioctl(filp, cmd, arg); + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + return ret; +} static const struct file_operations nouveau_driver_fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, - .unlocked_ioctl = drm_ioctl, + .unlocked_ioctl = nouveau_drm_ioctl, .mmap = nouveau_ttm_mmap, .poll = drm_poll, .fasync = drm_fasync, @@ -664,6 +708,60 @@ nouveau_drm_pci_table[] = { {} }; +static int nouveau_pmops_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + printk("runtime suspend called\n"); + + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + drm_kms_helper_poll_disable(drm_dev); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF, false); + nouveau_switcheroo_optimus_dsm(); + ret = nouveau_do_suspend(drm_dev); + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; + return ret; +} + +static int nouveau_pmops_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + printk("runtime resume called\n"); + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + ret = nouveau_do_resume(drm_dev); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF, false); + drm_kms_helper_poll_enable(drm_dev); + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + return ret; +} + +static int nouveau_pmops_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct drm_crtc *crtc; + + /* are we optimus enabled? */ + if (!nouveau_is_optimus()) { + DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + return -EBUSY; + } + + list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) { + if (crtc->enabled) { + DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); + return -EBUSY; + } + } + printk("runtime idle called\n"); + WARN_ON(1); + return 0; +} + static const struct dev_pm_ops nouveau_pm_ops = { .suspend = nouveau_pmops_suspend, .resume = nouveau_pmops_resume, @@ -671,6 +769,9 @@ static const struct dev_pm_ops nouveau_pm_ops = { .thaw = nouveau_pmops_thaw, .poweroff = nouveau_pmops_freeze, .restore = nouveau_pmops_resume, + .runtime_suspend = nouveau_pmops_runtime_suspend, + .runtime_resume = nouveau_pmops_runtime_resume, + .runtime_idle = nouveau_pmops_runtime_idle, }; static struct pci_driver diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index ab0c174..215c5ea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -129,8 +129,8 @@ nouveau_dev(struct drm_device *dev) return nv_device(nouveau_drm(dev)->device); } -int nouveau_drm_suspend(struct pci_dev *, pm_message_t); -int nouveau_drm_resume(struct pci_dev *); +int nouveau_pmops_suspend(struct device *dev); +int nouveau_pmops_resume(struct device *dev); #define NV_PRINTK(level, code, drm, fmt, args...) \ printk(level "nouveau " code "[ DRM][%s] " fmt, \ diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 9ca8afd..5938f65 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -23,7 +23,7 @@ */ #include - +#include #include "nouveau_drm.h" #include "nouveau_irq.h" #include "nv50_display.h" @@ -70,6 +70,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS) nv50_display_intr(dev); } + pm_runtime_mark_last_busy(dev->dev); return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 3c55ec2..509bb77 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -28,12 +28,10 @@ #include #include #include - #include "drmP.h" - #include "nouveau_drm.h" #include "nouveau_pm.h" - +#include "nouveau_acpi.h" #include #include #include diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 37fcc9d..47e23b5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -31,12 +31,12 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { struct drm_device *dev = pci_get_drvdata(pdev); - pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; - + if (nouveau_is_optimus() && state == VGA_SWITCHEROO_OFF) + return; if (state == VGA_SWITCHEROO_ON) { printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; - nouveau_drm_resume(pdev); + nouveau_pmops_resume(&pdev->dev); drm_kms_helper_poll_enable(dev); dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { @@ -44,7 +44,7 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(dev); nouveau_switcheroo_optimus_dsm(); - nouveau_drm_suspend(pdev, pmm); + nouveau_pmops_suspend(&pdev->dev); dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } @@ -80,7 +80,7 @@ nouveau_vga_init(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); - vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false); + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, nouveau_is_optimus()); } void diff --git a/include/drm/drmP.h b/include/drm/drmP.h index d6b67bb..698fb3e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1202,6 +1202,7 @@ struct drm_device { #define DRM_SWITCH_POWER_ON 0 #define DRM_SWITCH_POWER_OFF 1 #define DRM_SWITCH_POWER_CHANGING 2 +#define DRM_SWITCH_POWER_DYNAMIC_OFF 3 static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature)