summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2012-09-10 14:20:51 +1000
committerDave Airlie <airlied@gmail.com>2013-06-27 10:24:19 +1000
commite5396d183741d8b8e07887bedd4630470c35a3ae (patch)
tree48b8d4e0b8c30d8ac39c0fdee0c7ef440f795ef8
parent2da01fb02164a6949c358b5c26fadc1976db7ee4 (diff)
nouveau: add runtime PM support (v0.6)nv-pm-ops3-wip
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. v0.5: make sure we hit D3 properly fix fbdev_set_suspend locking interaction, we only will poweroff if we have no active crtcs/fbcon anyways. add reference for active crtcs. sprinkle mark last busy for autosuspend timeout v0.6: allow more flexible debugging - to avoid log spam add option to enable/disable dynpm got to D3Cold Signed-off-by: Dave Airlie <airlied@redhat.com Conflicts: drivers/gpu/drm/nouveau/nouveau_connector.c drivers/gpu/drm/nouveau/nouveau_drm.c drivers/gpu/drm/nouveau/nouveau_drm.h drivers/gpu/drm/nouveau/nouveau_pm.c drivers/gpu/drm/nouveau/nouveau_vga.c Conflicts: drivers/gpu/drm/nouveau/core/include/core/printk.h drivers/gpu/drm/nouveau/nouveau_drm.c Conflicts: drivers/gpu/drm/nouveau/nouveau_drm.c drivers/gpu/drm/nouveau/nouveau_irq.c
-rw-r--r--drivers/gpu/drm/nouveau/core/core/printk.c19
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/printk.h13
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/init.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c49
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c28
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c12
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c205
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c1174
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c14
11 files changed, 316 insertions, 1210 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/printk.c b/drivers/gpu/drm/nouveau/core/core/printk.c
index 6161eaf5447c..514c26a39449 100644
--- a/drivers/gpu/drm/nouveau/core/core/printk.c
+++ b/drivers/gpu/drm/nouveau/core/core/printk.c
@@ -27,6 +27,8 @@
#include <core/subdev.h>
#include <core/printk.h>
+int nv_printk_suspend_level = NV_DBG_DEBUG;
+
void
nv_printk_(struct nouveau_object *object, const char *pfx, int level,
const char *fmt, ...)
@@ -72,3 +74,20 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level,
vprintk(mfmt, args);
va_end(args);
}
+
+#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x
+
+const char *nv_printk_level_to_pfx(int level)
+{
+ switch (level) {
+ CONV_LEVEL(FATAL);
+ CONV_LEVEL(ERROR);
+ CONV_LEVEL(WARN);
+ CONV_LEVEL(INFO);
+ CONV_LEVEL(DEBUG);
+ CONV_LEVEL(PARANOIA);
+ CONV_LEVEL(TRACE);
+ CONV_LEVEL(SPAM);
+ }
+ return NV_PRINTK_DEBUG;
+}
diff --git a/drivers/gpu/drm/nouveau/core/include/core/printk.h b/drivers/gpu/drm/nouveau/core/include/core/printk.h
index febed2ea5c80..d87836e3a704 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/printk.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/printk.h
@@ -15,6 +15,12 @@ struct nouveau_object;
#define NV_PRINTK_TRACE KERN_DEBUG
#define NV_PRINTK_SPAM KERN_DEBUG
+extern int nv_printk_suspend_level;
+
+#define NV_DBG_SUSPEND (nv_printk_suspend_level)
+#define NV_PRINTK_SUSPEND (nv_printk_level_to_pfx(nv_printk_suspend_level))
+
+const char *nv_printk_level_to_pfx(int level);
void __printf(4, 5)
nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
@@ -31,6 +37,13 @@ nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
+#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a)
+
+static inline void nv_suspend_set_printk_level(int level)
+{
+ nv_printk_suspend_level = level;
+}
+
#define nv_assert(f,a...) do { \
if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \
nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a); \
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
index c434d398d16f..ad037c304fdc 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
@@ -2166,7 +2166,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
u16 data;
if (execute)
- nv_info(bios, "running init tables\n");
+ nv_suspend(bios, "running init tables\n");
while (!ret && (data = (init_script(bios, ++i)))) {
struct nvbios_init init = {
.subdev = subdev,
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 0782bd2f1e04..6413552df21c 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -22,6 +22,7 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -1007,13 +1008,59 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
return 0;
}
+int
+nouveau_crtc_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *dev;
+ struct nouveau_drm *drm;
+ int ret;
+ struct drm_crtc *crtc;
+ bool active = false;
+ if (!set || !set->crtc)
+ return -EINVAL;
+
+ dev = set->crtc->dev;
+
+ /* get a pm reference here */
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = drm_crtc_helper_set_config(set);
+
+ drm = nouveau_drm(dev);
+
+ /* if we get here with no crtcs active then we can drop a reference */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->enabled)
+ active = true;
+ }
+
+ pm_runtime_mark_last_busy(dev->dev);
+ /* if we have active crtcs and we don't have a power ref,
+ take the current one */
+ if (active && !drm->have_disp_power_ref) {
+ drm->have_disp_power_ref = true;
+ return ret;
+ }
+ /* if we have no active crtcs, then drop the power ref
+ we got before */
+ if (!active && drm->have_disp_power_ref) {
+ pm_runtime_put_autosuspend(dev->dev);
+ drm->have_disp_power_ref = false;
+ }
+ /* drop the power reference we got coming in here */
+ pm_runtime_put_autosuspend(dev->dev);
+ return ret;
+}
+
static const struct drm_crtc_funcs nv04_crtc_funcs = {
.save = nv_crtc_save,
.restore = nv_crtc_restore,
.cursor_set = nv04_crtc_cursor_set,
.cursor_move = nv04_crtc_cursor_move,
.gamma_set = nv_crtc_gamma_set,
- .set_config = drm_crtc_helper_set_config,
+ .set_config = nouveau_crtc_set_config,
.page_flip = nouveau_crtc_page_flip,
.destroy = nv_crtc_destroy,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 4da776f344d7..a8b2ff66e4c3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -26,6 +26,8 @@
#include <acpi/button.h>
+#include <linux/pm_runtime.h>
+
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
@@ -240,6 +242,8 @@ 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;
/* Cleanup the previous EDID block. */
if (nv_connector->edid) {
@@ -248,6 +252,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
nv_connector->edid = NULL;
}
+ ret = pm_runtime_get_sync(connector->dev->dev);
+ if (ret < 0)
+ return conn_status;
+
+
i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
if (i2c) {
nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
@@ -263,7 +272,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
@@ -290,13 +300,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:
@@ -311,12 +323,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_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index f17dc2ab03ec..ddd04b1bebfd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -384,7 +384,7 @@ nouveau_display_suspend(struct drm_device *dev)
nouveau_display_fini(dev);
- NV_INFO(drm, "unpinning framebuffer(s)...\n");
+ NV_SUSPEND(drm, "unpinning framebuffer(s)...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@@ -406,7 +406,7 @@ nouveau_display_suspend(struct drm_device *dev)
}
void
-nouveau_display_resume(struct drm_device *dev)
+nouveau_display_repin(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc;
@@ -431,10 +431,12 @@ nouveau_display_resume(struct drm_device *dev)
if (ret)
NV_ERROR(drm, "Could not pin/map cursor.\n");
}
+}
- nouveau_fbcon_set_suspend(dev, 0);
- nouveau_fbcon_zfill_all(dev);
-
+void
+nouveau_display_resume(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
nouveau_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index 1ea3e4734b62..229ac3f66c4c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -57,6 +57,7 @@ void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev);
int nouveau_display_suspend(struct drm_device *dev);
+void nouveau_display_repin(struct drm_device *dev);
void nouveau_display_resume(struct drm_device *dev);
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
@@ -73,6 +74,7 @@ int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *,
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
+int nouveau_crtc_set_config(struct drm_mode_set *set);
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
extern int nouveau_backlight_init(struct drm_device *);
extern void nouveau_backlight_exit(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 383f4e6ea9d1..a25b19e1cdd6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -25,7 +25,10 @@
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
-
+#include <linux/pm_runtime.h>
+#include <linux/vga_switcheroo.h>
+#include "drmP.h"
+#include "drm_crtc_helper.h"
#include <core/device.h>
#include <core/client.h>
#include <core/gpuobj.h>
@@ -69,6 +72,10 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, "
int nouveau_modeset = -1;
module_param_named(modeset, nouveau_modeset, int, 0400);
+MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)");
+int nouveau_runtime_pm = -1;
+module_param_named(runpm, nouveau_runtime_pm, int, 0400);
+
static struct drm_driver driver;
static int
@@ -379,6 +386,15 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
nouveau_accel_init(drm);
nouveau_fbcon_init(dev);
+
+ if (nouveau_runtime_pm != 0) {
+ 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_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+ }
return 0;
fail_dispinit:
@@ -392,6 +408,7 @@ fail_ttm:
nouveau_vga_fini(drm);
fail_device:
nouveau_cli_destroy(&drm->client);
+
return ret;
}
@@ -400,6 +417,7 @@ nouveau_drm_unload(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
+ pm_runtime_get_sync(dev->dev);
nouveau_fbcon_fini(dev);
nouveau_accel_fini(drm);
@@ -441,16 +459,13 @@ nouveau_do_suspend(struct drm_device *dev)
int ret;
if (dev->mode_config.num_crtc) {
- NV_INFO(drm, "suspending fbcon...\n");
- nouveau_fbcon_set_suspend(dev, 1);
-
- NV_INFO(drm, "suspending display...\n");
+ NV_SUSPEND(drm, "suspending display...\n");
ret = nouveau_display_suspend(dev);
if (ret)
return ret;
}
- NV_INFO(drm, "evicting buffers...\n");
+ NV_SUSPEND(drm, "evicting buffers...\n");
ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
NV_INFO(drm, "waiting for kernel channels to go idle...\n");
@@ -466,7 +481,7 @@ nouveau_do_suspend(struct drm_device *dev)
return ret;
}
- NV_INFO(drm, "suspending client object trees...\n");
+ NV_SUSPEND(drm, "suspending client object trees...\n");
if (drm->fence && nouveau_fence(drm)->suspend) {
if (!nouveau_fence(drm)->suspend(drm))
return -ENOMEM;
@@ -478,7 +493,7 @@ nouveau_do_suspend(struct drm_device *dev)
goto fail_client;
}
- NV_INFO(drm, "suspending kernel object tree...\n");
+ NV_SUSPEND(drm, "suspending kernel object tree...\n");
ret = nouveau_client_fini(&drm->client.base, true);
if (ret)
goto fail_client;
@@ -492,7 +507,7 @@ fail_client:
}
if (dev->mode_config.num_crtc) {
- NV_INFO(drm, "resuming display...\n");
+ NV_SUSPEND(drm, "resuming display...\n");
nouveau_display_resume(dev);
}
return ret;
@@ -504,9 +519,14 @@ int nouveau_pmops_suspend(struct device *dev)
struct drm_device *drm_dev = pci_get_drvdata(pdev);
int ret;
- if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
+ drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
return 0;
+ if (drm_dev->mode_config.num_crtc)
+ nouveau_fbcon_set_suspend(drm_dev, 1);
+
+ nv_suspend_set_printk_level(NV_DBG_INFO);
ret = nouveau_do_suspend(drm_dev);
if (ret)
return ret;
@@ -514,6 +534,7 @@ int nouveau_pmops_suspend(struct device *dev)
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
return 0;
}
@@ -524,15 +545,15 @@ nouveau_do_resume(struct drm_device *dev)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
- NV_INFO(drm, "re-enabling device...\n");
+ NV_SUSPEND(drm, "re-enabling device...\n");
nouveau_agp_reset(drm);
- NV_INFO(drm, "resuming kernel object tree...\n");
+ NV_SUSPEND(drm, "resuming kernel object tree...\n");
nouveau_client_init(&drm->client.base);
nouveau_agp_init(drm);
- NV_INFO(drm, "resuming client object trees...\n");
+ NV_SUSPEND(drm, "resuming client object trees...\n");
if (drm->fence && nouveau_fence(drm)->resume)
nouveau_fence(drm)->resume(drm);
@@ -544,9 +565,10 @@ nouveau_do_resume(struct drm_device *dev)
nouveau_pm_resume(dev);
if (dev->mode_config.num_crtc) {
- NV_INFO(drm, "resuming display...\n");
- nouveau_display_resume(dev);
+ NV_SUSPEND(drm, "resuming display...\n");
+ nouveau_display_repin(dev);
}
+
return 0;
}
@@ -556,7 +578,8 @@ int nouveau_pmops_resume(struct device *dev)
struct drm_device *drm_dev = pci_get_drvdata(pdev);
int ret;
- if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
+ drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
return 0;
pci_set_power_state(pdev, PCI_D0);
@@ -566,23 +589,54 @@ int nouveau_pmops_resume(struct device *dev)
return ret;
pci_set_master(pdev);
- return nouveau_do_resume(drm_dev);
+ nv_suspend_set_printk_level(NV_DBG_INFO);
+ ret = nouveau_do_resume(drm_dev);
+ if (ret) {
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
+ return ret;
+ }
+ if (drm_dev->mode_config.num_crtc)
+ nouveau_fbcon_set_suspend(drm_dev, 0);
+
+ nouveau_fbcon_zfill_all(drm_dev);
+ nouveau_display_resume(drm_dev);
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
+ return 0;
}
static int nouveau_pmops_freeze(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ int ret;
+
+ nv_suspend_set_printk_level(NV_DBG_INFO);
+ if (drm_dev->mode_config.num_crtc)
+ nouveau_fbcon_set_suspend(drm_dev, 1);
- return nouveau_do_suspend(drm_dev);
+ ret = nouveau_do_suspend(drm_dev);
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
+ return ret;
}
static int nouveau_pmops_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ int ret;
- return nouveau_do_resume(drm_dev);
+ nv_suspend_set_printk_level(NV_DBG_INFO);
+ ret = nouveau_do_resume(drm_dev);
+ if (ret) {
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
+ return ret;
+ }
+ if (drm_dev->mode_config.num_crtc)
+ nouveau_fbcon_set_suspend(drm_dev, 0);
+ nouveau_fbcon_zfill_all(drm_dev);
+ nouveau_display_resume(drm_dev);
+ nv_suspend_set_printk_level(NV_DBG_DEBUG);
+ return 0;
}
@@ -595,19 +649,24 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
char name[32], tmpname[TASK_COMM_LEN];
int ret;
+ /* need to bring up power immediately if opening device */
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ return ret;
+
get_task_comm(tmpname, current);
snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
ret = nouveau_cli_create(pdev, name, 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;
}
}
@@ -616,7 +675,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
@@ -625,12 +689,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
@@ -638,6 +705,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
@@ -656,12 +725,30 @@ 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_sync(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,
@@ -743,6 +830,75 @@ 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;
+
+ if (nouveau_runtime_pm == 0)
+ return -EINVAL;
+
+ drm_kms_helper_poll_disable(drm_dev);
+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+ nouveau_switcheroo_optimus_dsm();
+ ret = nouveau_do_suspend(drm_dev);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3cold);
+ 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;
+
+ if (nouveau_runtime_pm == 0)
+ return -EINVAL;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+ pci_set_master(pdev);
+
+ ret = nouveau_do_resume(drm_dev);
+ nouveau_display_resume(drm_dev);
+ drm_kms_helper_poll_enable(drm_dev);
+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+ 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;
+
+ if (nouveau_runtime_pm == 0)
+ return -EBUSY;
+
+ /* are we optimus enabled? */
+ if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
+ 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;
+ }
+ }
+ return 0;
+}
+
static const struct dev_pm_ops nouveau_pm_ops = {
.suspend = nouveau_pmops_suspend,
.resume = nouveau_pmops_resume,
@@ -750,6 +906,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 f2b30f89dee0..52328c2e0114 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -70,6 +70,8 @@ nouveau_cli(struct drm_file *fpriv)
return fpriv ? fpriv->driver_priv : NULL;
}
+extern int nouveau_runtime_pm;
+
struct nouveau_drm {
struct nouveau_cli client;
struct drm_device *dev;
@@ -128,6 +130,11 @@ struct nouveau_drm {
/* power management */
struct nouveau_pm *pm;
+
+ /* display power reference */
+ bool have_disp_power_ref;
+
+ struct dev_pm_domain vga_pm_domain;
};
static inline struct nouveau_drm *
@@ -145,6 +152,7 @@ nouveau_dev(struct drm_device *dev)
int nouveau_pmops_suspend(struct device *);
int nouveau_pmops_resume(struct device *);
+#define NV_SUSPEND(cli, fmt, args...) nv_suspend((cli), fmt, ##args)
#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
deleted file mode 100644
index 936b442a6ab7..000000000000
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ /dev/null
@@ -1,1174 +0,0 @@
-/*
- * Copyright 2010 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-
-#ifdef CONFIG_ACPI
-#include <linux/acpi.h>
-#endif
-#include <linux/power_supply.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <drm/drmP.h>
-
-#include "nouveau_drm.h"
-#include "nouveau_pm.h"
-
-#include <subdev/gpio.h>
-#include <subdev/timer.h>
-#include <subdev/therm.h>
-
-MODULE_PARM_DESC(perflvl, "Performance level (default: boot)");
-static char *nouveau_perflvl;
-module_param_named(perflvl, nouveau_perflvl, charp, 0400);
-
-MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)");
-static int nouveau_perflvl_wr;
-module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);
-
-static int
-nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,
- struct nouveau_pm_level *a, struct nouveau_pm_level *b)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- /*XXX: not on all boards, we should control based on temperature
- * on recent boards.. or maybe on some other factor we don't
- * know about?
- */
- if (therm && therm->fan_set &&
- a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {
- ret = therm->fan_set(therm, perflvl->fanspeed);
- if (ret && ret != -ENODEV) {
- NV_ERROR(drm, "fanspeed set failed: %d\n", ret);
- }
- }
-
- if (pm->voltage.supported && pm->voltage_set) {
- if (perflvl->volt_min && b->volt_min > a->volt_min) {
- ret = pm->voltage_set(dev, perflvl->volt_min);
- if (ret) {
- NV_ERROR(drm, "voltage set failed: %d\n", ret);
- return ret;
- }
- }
- }
-
- return 0;
-}
-
-static int
-nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- void *state;
- int ret;
-
- if (perflvl == pm->cur)
- return 0;
-
- ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);
- if (ret)
- return ret;
-
- state = pm->clocks_pre(dev, perflvl);
- if (IS_ERR(state)) {
- ret = PTR_ERR(state);
- goto error;
- }
- ret = pm->clocks_set(dev, state);
- if (ret)
- goto error;
-
- ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
- if (ret)
- return ret;
-
- pm->cur = perflvl;
- return 0;
-
-error:
- /* restore the fan speed and voltage before leaving */
- nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
- return ret;
-}
-
-void
-nouveau_pm_trigger(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_timer *ptimer = nouveau_timer(drm->device);
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_profile *profile = NULL;
- struct nouveau_pm_level *perflvl = NULL;
- int ret;
-
- /* select power profile based on current power source */
- if (power_supply_is_system_supplied())
- profile = pm->profile_ac;
- else
- profile = pm->profile_dc;
-
- if (profile != pm->profile) {
- pm->profile->func->fini(pm->profile);
- pm->profile = profile;
- pm->profile->func->init(pm->profile);
- }
-
- /* select performance level based on profile */
- perflvl = profile->func->select(profile);
-
- /* change perflvl, if necessary */
- if (perflvl != pm->cur) {
- u64 time0 = ptimer->read(ptimer);
-
- NV_INFO(drm, "setting performance level: %d", perflvl->id);
- ret = nouveau_pm_perflvl_set(dev, perflvl);
- if (ret)
- NV_INFO(drm, "> reclocking failed: %d\n\n", ret);
-
- NV_INFO(drm, "> reclocking took %lluns\n\n",
- ptimer->read(ptimer) - time0);
- }
-}
-
-static struct nouveau_pm_profile *
-profile_find(struct drm_device *dev, const char *string)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_profile *profile;
-
- list_for_each_entry(profile, &pm->profiles, head) {
- if (!strncmp(profile->name, string, sizeof(profile->name)))
- return profile;
- }
-
- return NULL;
-}
-
-static int
-nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_profile *ac = NULL, *dc = NULL;
- char string[16], *cur = string, *ptr;
-
- /* safety precaution, for now */
- if (nouveau_perflvl_wr != 7777)
- return -EPERM;
-
- strncpy(string, profile, sizeof(string));
- string[sizeof(string) - 1] = 0;
- if ((ptr = strchr(string, '\n')))
- *ptr = '\0';
-
- ptr = strsep(&cur, ",");
- if (ptr)
- ac = profile_find(dev, ptr);
-
- ptr = strsep(&cur, ",");
- if (ptr)
- dc = profile_find(dev, ptr);
- else
- dc = ac;
-
- if (ac == NULL || dc == NULL)
- return -EINVAL;
-
- pm->profile_ac = ac;
- pm->profile_dc = dc;
- nouveau_pm_trigger(dev);
- return 0;
-}
-
-static void
-nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)
-{
-}
-
-static struct nouveau_pm_level *
-nouveau_pm_static_select(struct nouveau_pm_profile *profile)
-{
- return container_of(profile, struct nouveau_pm_level, profile);
-}
-
-const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
- .destroy = nouveau_pm_static_dummy,
- .init = nouveau_pm_static_dummy,
- .fini = nouveau_pm_static_dummy,
- .select = nouveau_pm_static_select,
-};
-
-static int
-nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- memset(perflvl, 0, sizeof(*perflvl));
-
- if (pm->clocks_get) {
- ret = pm->clocks_get(dev, perflvl);
- if (ret)
- return ret;
- }
-
- if (pm->voltage.supported && pm->voltage_get) {
- ret = pm->voltage_get(dev);
- if (ret > 0) {
- perflvl->volt_min = ret;
- perflvl->volt_max = ret;
- }
- }
-
- if (therm && therm->fan_get) {
- ret = therm->fan_get(therm);
- if (ret >= 0)
- perflvl->fanspeed = ret;
- }
-
- nouveau_mem_timing_read(dev, &perflvl->timing);
- return 0;
-}
-
-static void
-nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
-{
- char c[16], s[16], v[32], f[16], m[16];
-
- c[0] = '\0';
- if (perflvl->core)
- snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
-
- s[0] = '\0';
- if (perflvl->shader)
- snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
-
- m[0] = '\0';
- if (perflvl->memory)
- snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
-
- v[0] = '\0';
- if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
- snprintf(v, sizeof(v), " voltage %dmV-%dmV",
- perflvl->volt_min / 1000, perflvl->volt_max / 1000);
- } else
- if (perflvl->volt_min) {
- snprintf(v, sizeof(v), " voltage %dmV",
- perflvl->volt_min / 1000);
- }
-
- f[0] = '\0';
- if (perflvl->fanspeed)
- snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
-
- snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);
-}
-
-static ssize_t
-nouveau_pm_get_perflvl_info(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct nouveau_pm_level *perflvl =
- container_of(a, struct nouveau_pm_level, dev_attr);
- char *ptr = buf;
- int len = PAGE_SIZE;
-
- snprintf(ptr, len, "%d:", perflvl->id);
- ptr += strlen(buf);
- len -= strlen(buf);
-
- nouveau_pm_perflvl_info(perflvl, ptr, len);
- return strlen(buf);
-}
-
-static ssize_t
-nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_level cur;
- int len = PAGE_SIZE, ret;
- char *ptr = buf;
-
- snprintf(ptr, len, "profile: %s, %s\nc:",
- pm->profile_ac->name, pm->profile_dc->name);
- ptr += strlen(buf);
- len -= strlen(buf);
-
- ret = nouveau_pm_perflvl_get(dev, &cur);
- if (ret == 0)
- nouveau_pm_perflvl_info(&cur, ptr, len);
- return strlen(buf);
-}
-
-static ssize_t
-nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
- int ret;
-
- ret = nouveau_pm_profile_set(dev, buf);
- if (ret)
- return ret;
- return strlen(buf);
-}
-
-static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
- nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
-
-static int
-nouveau_sysfs_init(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct device *d = &dev->pdev->dev;
- int ret, i;
-
- ret = device_create_file(d, &dev_attr_performance_level);
- if (ret)
- return ret;
-
- for (i = 0; i < pm->nr_perflvl; i++) {
- struct nouveau_pm_level *perflvl = &pm->perflvl[i];
-
- perflvl->dev_attr.attr.name = perflvl->name;
- perflvl->dev_attr.attr.mode = S_IRUGO;
- perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
- perflvl->dev_attr.store = NULL;
- sysfs_attr_init(&perflvl->dev_attr.attr);
-
- ret = device_create_file(d, &perflvl->dev_attr);
- if (ret) {
- NV_ERROR(drm, "failed pervlvl %d sysfs: %d\n",
- perflvl->id, i);
- perflvl->dev_attr.attr.name = NULL;
- nouveau_pm_fini(dev);
- return ret;
- }
- }
-
- return 0;
-}
-
-static void
-nouveau_sysfs_fini(struct drm_device *dev)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct device *d = &dev->pdev->dev;
- int i;
-
- device_remove_file(d, &dev_attr_performance_level);
- for (i = 0; i < pm->nr_perflvl; i++) {
- struct nouveau_pm_level *pl = &pm->perflvl[i];
-
- if (!pl->dev_attr.attr.name)
- break;
-
- device_remove_file(d, &pl->dev_attr);
- }
-}
-
-#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
-static ssize_t
-nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int temp = therm->temp_get(therm);
-
- if (temp < 0)
- return temp;
-
- return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000);
-}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
- NULL, 0);
-
-static ssize_t
-nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d,
- struct device_attribute *a, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", 100);
-}
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO,
- nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0);
-
-static ssize_t
-nouveau_hwmon_temp1_auto_point1_temp(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,
- struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST,
- value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR,
- nouveau_hwmon_temp1_auto_point1_temp,
- nouveau_hwmon_set_temp1_auto_point1_temp, 0);
-
-static ssize_t
-nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,
- struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST,
- value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
- nouveau_hwmon_temp1_auto_point1_temp_hyst,
- nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0);
-
-static ssize_t
-nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK, value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
- nouveau_hwmon_set_max_temp,
- 0);
-
-static ssize_t
-nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST,
- value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
- nouveau_hwmon_max_temp_hyst,
- nouveau_hwmon_set_max_temp_hyst, 0);
-
-static ssize_t
-nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
- const char *buf,
- size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL, value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
- nouveau_hwmon_critical_temp,
- nouveau_hwmon_set_critical_temp,
- 0);
-
-static ssize_t
-nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_critical_temp_hyst(struct device *d,
- struct device_attribute *a,
- const char *buf,
- size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST,
- value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
- nouveau_hwmon_critical_temp_hyst,
- nouveau_hwmon_set_critical_temp_hyst, 0);
-static ssize_t
-nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a,
- const char *buf,
- size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR,
- nouveau_hwmon_emergency_temp,
- nouveau_hwmon_set_emergency_temp,
- 0);
-
-static ssize_t
-nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000);
-}
-static ssize_t
-nouveau_hwmon_set_emergency_temp_hyst(struct device *d,
- struct device_attribute *a,
- const char *buf,
- size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return count;
-
- therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST,
- value / 1000);
-
- return count;
-}
-static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR,
- nouveau_hwmon_emergency_temp_hyst,
- nouveau_hwmon_set_emergency_temp_hyst,
- 0);
-
-static ssize_t nouveau_hwmon_show_name(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "nouveau\n");
-}
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
-
-static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "1000\n");
-}
-static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
- nouveau_hwmon_show_update_rate,
- NULL, 0);
-
-static ssize_t
-nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
-}
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input,
- NULL, 0);
-
- static ssize_t
-nouveau_hwmon_get_pwm1_enable(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MODE);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t
-nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
- int ret;
-
- if (strict_strtol(buf, 10, &value) == -EINVAL)
- return -EINVAL;
-
- ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value);
- if (ret)
- return ret;
- else
- return count;
-}
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
- nouveau_hwmon_get_pwm1_enable,
- nouveau_hwmon_set_pwm1_enable, 0);
-
-static ssize_t
-nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- ret = therm->fan_get(therm);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t
-nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret = -ENODEV;
- long value;
-
- if (nouveau_perflvl_wr != 7777)
- return -EPERM;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return -EINVAL;
-
- ret = therm->fan_set(therm, value);
- if (ret)
- return ret;
-
- return count;
-}
-
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR,
- nouveau_hwmon_get_pwm1,
- nouveau_hwmon_set_pwm1, 0);
-
-static ssize_t
-nouveau_hwmon_get_pwm1_min(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t
-nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
- int ret;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return -EINVAL;
-
- ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY, value);
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR,
- nouveau_hwmon_get_pwm1_min,
- nouveau_hwmon_set_pwm1_min, 0);
-
-static ssize_t
-nouveau_hwmon_get_pwm1_max(struct device *d,
- struct device_attribute *a, char *buf)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- int ret;
-
- ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t
-nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a,
- const char *buf, size_t count)
-{
- struct drm_device *dev = dev_get_drvdata(d);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- long value;
- int ret;
-
- if (kstrtol(buf, 10, &value) == -EINVAL)
- return -EINVAL;
-
- ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY, value);
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,
- nouveau_hwmon_get_pwm1_max,
- nouveau_hwmon_set_pwm1_max, 0);
-
-static struct attribute *hwmon_default_attributes[] = {
- &sensor_dev_attr_name.dev_attr.attr,
- &sensor_dev_attr_update_rate.dev_attr.attr,
- NULL
-};
-static struct attribute *hwmon_temp_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,
- &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
- &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
- NULL
-};
-static struct attribute *hwmon_fan_rpm_attributes[] = {
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- NULL
-};
-static struct attribute *hwmon_pwm_fan_attributes[] = {
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm1_min.dev_attr.attr,
- &sensor_dev_attr_pwm1_max.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group hwmon_default_attrgroup = {
- .attrs = hwmon_default_attributes,
-};
-static const struct attribute_group hwmon_temp_attrgroup = {
- .attrs = hwmon_temp_attributes,
-};
-static const struct attribute_group hwmon_fan_rpm_attrgroup = {
- .attrs = hwmon_fan_rpm_attributes,
-};
-static const struct attribute_group hwmon_pwm_fan_attrgroup = {
- .attrs = hwmon_pwm_fan_attributes,
-};
-#endif
-
-static int
-nouveau_hwmon_init(struct drm_device *dev)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
-
-#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
- struct device *hwmon_dev;
- int ret = 0;
-
- if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set)
- return -ENODEV;
-
- hwmon_dev = hwmon_device_register(&dev->pdev->dev);
- if (IS_ERR(hwmon_dev)) {
- ret = PTR_ERR(hwmon_dev);
- NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret);
- return ret;
- }
- dev_set_drvdata(hwmon_dev, dev);
-
- /* set the default attributes */
- ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup);
- if (ret) {
- if (ret)
- goto error;
- }
-
- /* if the card has a working thermal sensor */
- if (therm->temp_get(therm) >= 0) {
- ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup);
- if (ret) {
- if (ret)
- goto error;
- }
- }
-
- /* if the card has a pwm fan */
- /*XXX: incorrect, need better detection for this, some boards have
- * the gpio entries for pwm fan control even when there's no
- * actual fan connected to it... therm table? */
- if (therm->fan_get && therm->fan_get(therm) >= 0) {
- ret = sysfs_create_group(&hwmon_dev->kobj,
- &hwmon_pwm_fan_attrgroup);
- if (ret)
- goto error;
- }
-
- /* if the card can read the fan rpm */
- if (therm->fan_sense(therm) >= 0) {
- ret = sysfs_create_group(&hwmon_dev->kobj,
- &hwmon_fan_rpm_attrgroup);
- if (ret)
- goto error;
- }
-
- pm->hwmon = hwmon_dev;
-
- return 0;
-
-error:
- NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret);
- hwmon_device_unregister(hwmon_dev);
- pm->hwmon = NULL;
- return ret;
-#else
- pm->hwmon = NULL;
- return 0;
-#endif
-}
-
-static void
-nouveau_hwmon_fini(struct drm_device *dev)
-{
-#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
- struct nouveau_pm *pm = nouveau_pm(dev);
-
- if (pm->hwmon) {
- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup);
- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup);
- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup);
- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup);
-
- hwmon_device_unregister(pm->hwmon);
- }
-#endif
-}
-
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
-static int
-nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
-{
- struct nouveau_pm *pm = container_of(nb, struct nouveau_pm, acpi_nb);
- struct nouveau_drm *drm = nouveau_drm(pm->dev);
- struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
-
- if (strcmp(entry->device_class, "ac_adapter") == 0) {
- bool ac = power_supply_is_system_supplied();
-
- NV_DEBUG(drm, "power supply changed: %s\n", ac ? "AC" : "DC");
- nouveau_pm_trigger(pm->dev);
- }
-
- return NOTIFY_OK;
-}
-#endif
-
-int
-nouveau_pm_init(struct drm_device *dev)
-{
- struct nouveau_device *device = nouveau_dev(dev);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_pm *pm;
- char info[256];
- int ret, i;
-
- pm = drm->pm = kzalloc(sizeof(*pm), GFP_KERNEL);
- if (!pm)
- return -ENOMEM;
-
- pm->dev = dev;
-
- if (device->card_type < NV_40) {
- pm->clocks_get = nv04_pm_clocks_get;
- pm->clocks_pre = nv04_pm_clocks_pre;
- pm->clocks_set = nv04_pm_clocks_set;
- if (nouveau_gpio(drm->device)) {
- pm->voltage_get = nouveau_voltage_gpio_get;
- pm->voltage_set = nouveau_voltage_gpio_set;
- }
- } else
- if (device->card_type < NV_50) {
- pm->clocks_get = nv40_pm_clocks_get;
- pm->clocks_pre = nv40_pm_clocks_pre;
- pm->clocks_set = nv40_pm_clocks_set;
- pm->voltage_get = nouveau_voltage_gpio_get;
- pm->voltage_set = nouveau_voltage_gpio_set;
- } else
- if (device->card_type < NV_C0) {
- if (device->chipset < 0xa3 ||
- device->chipset == 0xaa ||
- device->chipset == 0xac) {
- pm->clocks_get = nv50_pm_clocks_get;
- pm->clocks_pre = nv50_pm_clocks_pre;
- pm->clocks_set = nv50_pm_clocks_set;
- } else {
- pm->clocks_get = nva3_pm_clocks_get;
- pm->clocks_pre = nva3_pm_clocks_pre;
- pm->clocks_set = nva3_pm_clocks_set;
- }
- pm->voltage_get = nouveau_voltage_gpio_get;
- pm->voltage_set = nouveau_voltage_gpio_set;
- } else
- if (device->card_type < NV_E0) {
- pm->clocks_get = nvc0_pm_clocks_get;
- pm->clocks_pre = nvc0_pm_clocks_pre;
- pm->clocks_set = nvc0_pm_clocks_set;
- pm->voltage_get = nouveau_voltage_gpio_get;
- pm->voltage_set = nouveau_voltage_gpio_set;
- }
-
-
- /* parse aux tables from vbios */
- nouveau_volt_init(dev);
-
- INIT_LIST_HEAD(&pm->profiles);
-
- /* determine current ("boot") performance level */
- ret = nouveau_pm_perflvl_get(dev, &pm->boot);
- if (ret) {
- NV_ERROR(drm, "failed to determine boot perflvl\n");
- return ret;
- }
-
- strncpy(pm->boot.name, "boot", 4);
- strncpy(pm->boot.profile.name, "boot", 4);
- pm->boot.profile.func = &nouveau_pm_static_profile_func;
-
- list_add(&pm->boot.profile.head, &pm->profiles);
-
- pm->profile_ac = &pm->boot.profile;
- pm->profile_dc = &pm->boot.profile;
- pm->profile = &pm->boot.profile;
- pm->cur = &pm->boot;
-
- /* add performance levels from vbios */
- nouveau_perf_init(dev);
-
- /* display available performance levels */
- NV_INFO(drm, "%d available performance level(s)\n", pm->nr_perflvl);
- for (i = 0; i < pm->nr_perflvl; i++) {
- nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
- NV_INFO(drm, "%d:%s", pm->perflvl[i].id, info);
- }
-
- nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
- NV_INFO(drm, "c:%s", info);
-
- /* switch performance levels now if requested */
- if (nouveau_perflvl != NULL)
- nouveau_pm_profile_set(dev, nouveau_perflvl);
-
- nouveau_sysfs_init(dev);
- nouveau_hwmon_init(dev);
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
- pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
- register_acpi_notifier(&pm->acpi_nb);
-#endif
-
- return 0;
-}
-
-void
-nouveau_pm_fini(struct drm_device *dev)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_profile *profile, *tmp;
-
- list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {
- list_del(&profile->head);
- profile->func->destroy(profile);
- }
-
- if (pm->cur != &pm->boot)
- nouveau_pm_perflvl_set(dev, &pm->boot);
-
- nouveau_perf_fini(dev);
- nouveau_volt_fini(dev);
-
-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
- unregister_acpi_notifier(&pm->acpi_nb);
-#endif
- nouveau_hwmon_fini(dev);
- nouveau_sysfs_fini(dev);
-
- nouveau_drm(dev)->pm = NULL;
- kfree(pm);
-}
-
-void
-nouveau_pm_resume(struct drm_device *dev)
-{
- struct nouveau_pm *pm = nouveau_pm(dev);
- struct nouveau_pm_level *perflvl;
-
- if (!pm->cur || pm->cur == &pm->boot)
- return;
-
- perflvl = pm->cur;
- pm->cur = &pm->boot;
- nouveau_pm_perflvl_set(dev, perflvl);
-}
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 40a09f11a600..81638d7f2eff 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -32,6 +32,9 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
{
struct drm_device *dev = pci_get_drvdata(pdev);
+ if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && 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;
@@ -78,8 +81,17 @@ void
nouveau_vga_init(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
+ bool runtime = false;
vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
- vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false);
+
+ if (nouveau_runtime_pm == 1)
+ runtime = true;
+ if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
+ runtime = true;
+ vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime);
+
+ if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
+ vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain);
}
void