aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2012-09-10 00:20:51 -0400
committerDave Airlie <airlied@redhat.com>2013-08-28 23:30:46 -0400
commit5addcf0a5f0fadceba6bd562d0616a1c5d4c1a4d (patch)
tree5162dd3bf9dd94b2b59b01a320fc019deda052bd
parent13bb9cc8726c716a7f271fc2c760ba15e1fdd38c (diff)
nouveau: add runtime PM support (v0.9)
This hooks nouveau up to the runtime PM system to enable dynamic power management for secondary GPUs in switchable and optimus laptops. a) rewrite suspend/resume printks to hide them during dynamic s/r to avoid cluttering logs b) add runtime pm suspend to irq handler, crtc display, ioctl handler, connector status, c) handle hdmi audio dynamic power on/off using magic register. 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 v0.7: add hdmi audio support. v0.8: call autosuspend from idle, so pci config space access doesn't go straight back to sleep, this makes starting X faster. only signal usage if we actually handle the irq, otherwise usb keeps us awake. fix nv50 display active powerdown v0.9: use masking function to enable hdmi audio set busy when we fail to suspend Signed-off-by: Dave Airlie <airlied@redhat.com>
-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/core/subdev/mc/base.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c49
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c42
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c27
-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.c250
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.h9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c14
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c2
13 files changed, 404 insertions, 43 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/printk.c b/drivers/gpu/drm/nouveau/core/core/printk.c
index 6161eaf5447c..52fb2aa129e8 100644
--- a/drivers/gpu/drm/nouveau/core/core/printk.c
+++ b/drivers/gpu/drm/nouveau/core/core/printk.c
@@ -27,6 +27,8 @@
27#include <core/subdev.h> 27#include <core/subdev.h>
28#include <core/printk.h> 28#include <core/printk.h>
29 29
30int nv_printk_suspend_level = NV_DBG_DEBUG;
31
30void 32void
31nv_printk_(struct nouveau_object *object, const char *pfx, int level, 33nv_printk_(struct nouveau_object *object, const char *pfx, int level,
32 const char *fmt, ...) 34 const char *fmt, ...)
@@ -72,3 +74,20 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level,
72 vprintk(mfmt, args); 74 vprintk(mfmt, args);
73 va_end(args); 75 va_end(args);
74} 76}
77
78#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x
79
80const char *nv_printk_level_to_pfx(int level)
81{
82 switch (level) {
83 CONV_LEVEL(FATAL);
84 CONV_LEVEL(ERROR);
85 CONV_LEVEL(WARN);
86 CONV_LEVEL(INFO);
87 CONV_LEVEL(DEBUG);
88 CONV_LEVEL(PARANOIA);
89 CONV_LEVEL(TRACE);
90 CONV_LEVEL(SPAM);
91 }
92 return NV_PRINTK_DEBUG;
93}
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;
15#define NV_PRINTK_TRACE KERN_DEBUG 15#define NV_PRINTK_TRACE KERN_DEBUG
16#define NV_PRINTK_SPAM KERN_DEBUG 16#define NV_PRINTK_SPAM KERN_DEBUG
17 17
18extern int nv_printk_suspend_level;
19
20#define NV_DBG_SUSPEND (nv_printk_suspend_level)
21#define NV_PRINTK_SUSPEND (nv_printk_level_to_pfx(nv_printk_suspend_level))
22
23const char *nv_printk_level_to_pfx(int level);
18void __printf(4, 5) 24void __printf(4, 5)
19nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); 25nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
20 26
@@ -31,6 +37,13 @@ nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
31#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a) 37#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
32#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a) 38#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
33 39
40#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a)
41
42static inline void nv_suspend_set_printk_level(int level)
43{
44 nv_printk_suspend_level = level;
45}
46
34#define nv_assert(f,a...) do { \ 47#define nv_assert(f,a...) do { \
35 if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \ 48 if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \
36 nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a); \ 49 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 0687e6481438..2e11ea02cf87 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
@@ -2165,7 +2165,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
2165 u16 data; 2165 u16 data;
2166 2166
2167 if (execute) 2167 if (execute)
2168 nv_info(bios, "running init tables\n"); 2168 nv_suspend(bios, "running init tables\n");
2169 while (!ret && (data = (init_script(bios, ++i)))) { 2169 while (!ret && (data = (init_script(bios, ++i)))) {
2170 struct nvbios_init init = { 2170 struct nvbios_init init = {
2171 .subdev = subdev, 2171 .subdev = subdev,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
index 1c0330b8c9a4..2e7c5fd3de3d 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
@@ -23,16 +23,20 @@
23 */ 23 */
24 24
25#include <subdev/mc.h> 25#include <subdev/mc.h>
26#include <linux/pm_runtime.h>
26 27
27static irqreturn_t 28static irqreturn_t
28nouveau_mc_intr(int irq, void *arg) 29nouveau_mc_intr(int irq, void *arg)
29{ 30{
30 struct nouveau_mc *pmc = arg; 31 struct nouveau_mc *pmc = arg;
31 const struct nouveau_mc_intr *map = pmc->intr_map; 32 const struct nouveau_mc_intr *map = pmc->intr_map;
33 struct nouveau_device *device = nv_device(pmc);
32 struct nouveau_subdev *unit; 34 struct nouveau_subdev *unit;
33 u32 stat, intr; 35 u32 stat, intr;
34 36
35 intr = stat = nv_rd32(pmc, 0x000100); 37 intr = stat = nv_rd32(pmc, 0x000100);
38 if (intr == 0xffffffff)
39 return IRQ_NONE;
36 while (stat && map->stat) { 40 while (stat && map->stat) {
37 if (stat & map->stat) { 41 if (stat & map->stat) {
38 unit = nouveau_subdev(pmc, map->unit); 42 unit = nouveau_subdev(pmc, map->unit);
@@ -47,6 +51,8 @@ nouveau_mc_intr(int irq, void *arg)
47 nv_error(pmc, "unknown intr 0x%08x\n", stat); 51 nv_error(pmc, "unknown intr 0x%08x\n", stat);
48 } 52 }
49 53
54 if (stat == IRQ_HANDLED)
55 pm_runtime_mark_last_busy(&device->pdev->dev);
50 return stat ? IRQ_HANDLED : IRQ_NONE; 56 return stat ? IRQ_HANDLED : IRQ_NONE;
51} 57}
52 58
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 @@
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE. 23 * DEALINGS IN THE SOFTWARE.
24 */ 24 */
25#include <linux/pm_runtime.h>
25 26
26#include <drm/drmP.h> 27#include <drm/drmP.h>
27#include <drm/drm_crtc_helper.h> 28#include <drm/drm_crtc_helper.h>
@@ -1007,13 +1008,59 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
1007 return 0; 1008 return 0;
1008} 1009}
1009 1010
1011int
1012nouveau_crtc_set_config(struct drm_mode_set *set)
1013{
1014 struct drm_device *dev;
1015 struct nouveau_drm *drm;
1016 int ret;
1017 struct drm_crtc *crtc;
1018 bool active = false;
1019 if (!set || !set->crtc)
1020 return -EINVAL;
1021
1022 dev = set->crtc->dev;
1023
1024 /* get a pm reference here */
1025 ret = pm_runtime_get_sync(dev->dev);
1026 if (ret < 0)
1027 return ret;
1028
1029 ret = drm_crtc_helper_set_config(set);
1030
1031 drm = nouveau_drm(dev);
1032
1033 /* if we get here with no crtcs active then we can drop a reference */
1034 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1035 if (crtc->enabled)
1036 active = true;
1037 }
1038
1039 pm_runtime_mark_last_busy(dev->dev);
1040 /* if we have active crtcs and we don't have a power ref,
1041 take the current one */
1042 if (active && !drm->have_disp_power_ref) {
1043 drm->have_disp_power_ref = true;
1044 return ret;
1045 }
1046 /* if we have no active crtcs, then drop the power ref
1047 we got before */
1048 if (!active && drm->have_disp_power_ref) {
1049 pm_runtime_put_autosuspend(dev->dev);
1050 drm->have_disp_power_ref = false;
1051 }
1052 /* drop the power reference we got coming in here */
1053 pm_runtime_put_autosuspend(dev->dev);
1054 return ret;
1055}
1056
1010static const struct drm_crtc_funcs nv04_crtc_funcs = { 1057static const struct drm_crtc_funcs nv04_crtc_funcs = {
1011 .save = nv_crtc_save, 1058 .save = nv_crtc_save,
1012 .restore = nv_crtc_restore, 1059 .restore = nv_crtc_restore,
1013 .cursor_set = nv04_crtc_cursor_set, 1060 .cursor_set = nv04_crtc_cursor_set,
1014 .cursor_move = nv04_crtc_cursor_move, 1061 .cursor_move = nv04_crtc_cursor_move,
1015 .gamma_set = nv_crtc_gamma_set, 1062 .gamma_set = nv_crtc_gamma_set,
1016 .set_config = drm_crtc_helper_set_config, 1063 .set_config = nouveau_crtc_set_config,
1017 .page_flip = nouveau_crtc_page_flip, 1064 .page_flip = nouveau_crtc_page_flip,
1018 .destroy = nv_crtc_destroy, 1065 .destroy = nv_crtc_destroy,
1019}; 1066};
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index d97f20069d3e..dd7d2e182719 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -25,8 +25,27 @@
25#define NOUVEAU_DSM_POWER_SPEED 0x01 25#define NOUVEAU_DSM_POWER_SPEED 0x01
26#define NOUVEAU_DSM_POWER_STAMINA 0x02 26#define NOUVEAU_DSM_POWER_STAMINA 0x02
27 27
28#define NOUVEAU_DSM_OPTIMUS_FN 0x1A 28#define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
29#define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001 29#define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
30
31#define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
32#define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
33#define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
34
35#define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
36
37/* result of the optimus caps function */
38#define OPTIMUS_ENABLED (1 << 0)
39#define OPTIMUS_STATUS_MASK (3 << 3)
40#define OPTIMUS_STATUS_OFF (0 << 3)
41#define OPTIMUS_STATUS_ON_ENABLED (1 << 3)
42#define OPTIMUS_STATUS_PWR_STABLE (3 << 3)
43#define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
44#define OPTIMUS_CAPS_MASK (7 << 24)
45#define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
46
47#define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
48#define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
30 49
31static struct nouveau_dsm_priv { 50static struct nouveau_dsm_priv {
32 bool dsm_detected; 51 bool dsm_detected;
@@ -251,9 +270,18 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
251 retval |= NOUVEAU_DSM_HAS_MUX; 270 retval |= NOUVEAU_DSM_HAS_MUX;
252 271
253 if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm, 272 if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm,
254 NOUVEAU_DSM_OPTIMUS_FN)) 273 NOUVEAU_DSM_OPTIMUS_CAPS))
255 retval |= NOUVEAU_DSM_HAS_OPT; 274 retval |= NOUVEAU_DSM_HAS_OPT;
256 275
276 if (retval & NOUVEAU_DSM_HAS_OPT) {
277 uint32_t result;
278 nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
279 &result);
280 dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
281 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
282 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
283 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
284 }
257 if (retval) 285 if (retval)
258 nouveau_dsm_priv.dhandle = dhandle; 286 nouveau_dsm_priv.dhandle = dhandle;
259 287
@@ -328,8 +356,12 @@ void nouveau_switcheroo_optimus_dsm(void)
328 if (!nouveau_dsm_priv.optimus_detected) 356 if (!nouveau_dsm_priv.optimus_detected)
329 return; 357 return;
330 358
331 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN, 359 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
332 NOUVEAU_DSM_OPTIMUS_ARGS, &result); 360 0x3, &result);
361
362 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
363 NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
364
333} 365}
334 366
335void nouveau_unregister_dsm_handler(void) 367void nouveau_unregister_dsm_handler(void)
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 4da776f344d7..c5b36f9e9a10 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -26,6 +26,8 @@
26 26
27#include <acpi/button.h> 27#include <acpi/button.h>
28 28
29#include <linux/pm_runtime.h>
30
29#include <drm/drmP.h> 31#include <drm/drmP.h>
30#include <drm/drm_edid.h> 32#include <drm/drm_edid.h>
31#include <drm/drm_crtc_helper.h> 33#include <drm/drm_crtc_helper.h>
@@ -240,6 +242,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
240 struct nouveau_encoder *nv_partner; 242 struct nouveau_encoder *nv_partner;
241 struct nouveau_i2c_port *i2c; 243 struct nouveau_i2c_port *i2c;
242 int type; 244 int type;
245 int ret;
246 enum drm_connector_status conn_status = connector_status_disconnected;
243 247
244 /* Cleanup the previous EDID block. */ 248 /* Cleanup the previous EDID block. */
245 if (nv_connector->edid) { 249 if (nv_connector->edid) {
@@ -248,6 +252,10 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
248 nv_connector->edid = NULL; 252 nv_connector->edid = NULL;
249 } 253 }
250 254
255 ret = pm_runtime_get_sync(connector->dev->dev);
256 if (ret < 0)
257 return conn_status;
258
251 i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); 259 i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
252 if (i2c) { 260 if (i2c) {
253 nv_connector->edid = drm_get_edid(connector, &i2c->adapter); 261 nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
@@ -263,7 +271,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
263 !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { 271 !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
264 NV_ERROR(drm, "Detected %s, but failed init\n", 272 NV_ERROR(drm, "Detected %s, but failed init\n",
265 drm_get_connector_name(connector)); 273 drm_get_connector_name(connector));
266 return connector_status_disconnected; 274 conn_status = connector_status_disconnected;
275 goto out;
267 } 276 }
268 277
269 /* Override encoder type for DVI-I based on whether EDID 278 /* Override encoder type for DVI-I based on whether EDID
@@ -290,13 +299,15 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
290 } 299 }
291 300
292 nouveau_connector_set_encoder(connector, nv_encoder); 301 nouveau_connector_set_encoder(connector, nv_encoder);
293 return connector_status_connected; 302 conn_status = connector_status_connected;
303 goto out;
294 } 304 }
295 305
296 nv_encoder = nouveau_connector_of_detect(connector); 306 nv_encoder = nouveau_connector_of_detect(connector);
297 if (nv_encoder) { 307 if (nv_encoder) {
298 nouveau_connector_set_encoder(connector, nv_encoder); 308 nouveau_connector_set_encoder(connector, nv_encoder);
299 return connector_status_connected; 309 conn_status = connector_status_connected;
310 goto out;
300 } 311 }
301 312
302detect_analog: 313detect_analog:
@@ -311,12 +322,18 @@ detect_analog:
311 if (helper->detect(encoder, connector) == 322 if (helper->detect(encoder, connector) ==
312 connector_status_connected) { 323 connector_status_connected) {
313 nouveau_connector_set_encoder(connector, nv_encoder); 324 nouveau_connector_set_encoder(connector, nv_encoder);
314 return connector_status_connected; 325 conn_status = connector_status_connected;
326 goto out;
315 } 327 }
316 328
317 } 329 }
318 330
319 return connector_status_disconnected; 331 out:
332
333 pm_runtime_mark_last_busy(connector->dev->dev);
334 pm_runtime_put_autosuspend(connector->dev->dev);
335
336 return conn_status;
320} 337}
321 338
322static enum drm_connector_status 339static enum drm_connector_status
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 78637afb9b94..dbcf10681ab2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -394,7 +394,7 @@ nouveau_display_suspend(struct drm_device *dev)
394 394
395 nouveau_display_fini(dev); 395 nouveau_display_fini(dev);
396 396
397 NV_INFO(drm, "unpinning framebuffer(s)...\n"); 397 NV_SUSPEND(drm, "unpinning framebuffer(s)...\n");
398 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 398 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
399 struct nouveau_framebuffer *nouveau_fb; 399 struct nouveau_framebuffer *nouveau_fb;
400 400
@@ -416,7 +416,7 @@ nouveau_display_suspend(struct drm_device *dev)
416} 416}
417 417
418void 418void
419nouveau_display_resume(struct drm_device *dev) 419nouveau_display_repin(struct drm_device *dev)
420{ 420{
421 struct nouveau_drm *drm = nouveau_drm(dev); 421 struct nouveau_drm *drm = nouveau_drm(dev);
422 struct drm_crtc *crtc; 422 struct drm_crtc *crtc;
@@ -441,10 +441,12 @@ nouveau_display_resume(struct drm_device *dev)
441 if (ret) 441 if (ret)
442 NV_ERROR(drm, "Could not pin/map cursor.\n"); 442 NV_ERROR(drm, "Could not pin/map cursor.\n");
443 } 443 }
444}
444 445
445 nouveau_fbcon_set_suspend(dev, 0); 446void
446 nouveau_fbcon_zfill_all(dev); 447nouveau_display_resume(struct drm_device *dev)
447 448{
449 struct drm_crtc *crtc;
448 nouveau_display_init(dev); 450 nouveau_display_init(dev);
449 451
450 /* Force CLUT to get re-loaded during modeset */ 452 /* 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 185e74132a6d..da84f1f40ec2 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);
57int nouveau_display_init(struct drm_device *dev); 57int nouveau_display_init(struct drm_device *dev);
58void nouveau_display_fini(struct drm_device *dev); 58void nouveau_display_fini(struct drm_device *dev);
59int nouveau_display_suspend(struct drm_device *dev); 59int nouveau_display_suspend(struct drm_device *dev);
60void nouveau_display_repin(struct drm_device *dev);
60void nouveau_display_resume(struct drm_device *dev); 61void nouveau_display_resume(struct drm_device *dev);
61 62
62int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, 63int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
@@ -71,6 +72,7 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
71 72
72void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); 73void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
73 74
75int nouveau_crtc_set_config(struct drm_mode_set *set);
74#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT 76#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
75extern int nouveau_backlight_init(struct drm_device *); 77extern int nouveau_backlight_init(struct drm_device *);
76extern void nouveau_backlight_exit(struct drm_device *); 78extern 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 b29d04b822ae..62c6118e94c0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -25,7 +25,10 @@
25#include <linux/console.h> 25#include <linux/console.h>
26#include <linux/module.h> 26#include <linux/module.h>
27#include <linux/pci.h> 27#include <linux/pci.h>
28 28#include <linux/pm_runtime.h>
29#include <linux/vga_switcheroo.h>
30#include "drmP.h"
31#include "drm_crtc_helper.h"
29#include <core/device.h> 32#include <core/device.h>
30#include <core/client.h> 33#include <core/client.h>
31#include <core/gpuobj.h> 34#include <core/gpuobj.h>
@@ -69,6 +72,10 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, "
69int nouveau_modeset = -1; 72int nouveau_modeset = -1;
70module_param_named(modeset, nouveau_modeset, int, 0400); 73module_param_named(modeset, nouveau_modeset, int, 0400);
71 74
75MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)");
76int nouveau_runtime_pm = -1;
77module_param_named(runpm, nouveau_runtime_pm, int, 0400);
78
72static struct drm_driver driver; 79static struct drm_driver driver;
73 80
74static int 81static int
@@ -296,6 +303,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
296 return 0; 303 return 0;
297} 304}
298 305
306#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
307
308static void
309nouveau_get_hdmi_dev(struct drm_device *dev)
310{
311 struct nouveau_drm *drm = dev->dev_private;
312 struct pci_dev *pdev = dev->pdev;
313
314 /* subfunction one is a hdmi audio device? */
315 drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
316 PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
317
318 if (!drm->hdmi_device) {
319 DRM_INFO("hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
320 return;
321 }
322
323 if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
324 DRM_INFO("possible hdmi device not audio %d\n", drm->hdmi_device->class);
325 pci_dev_put(drm->hdmi_device);
326 drm->hdmi_device = NULL;
327 return;
328 }
329}
330
299static int 331static int
300nouveau_drm_load(struct drm_device *dev, unsigned long flags) 332nouveau_drm_load(struct drm_device *dev, unsigned long flags)
301{ 333{
@@ -314,6 +346,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
314 INIT_LIST_HEAD(&drm->clients); 346 INIT_LIST_HEAD(&drm->clients);
315 spin_lock_init(&drm->tile.lock); 347 spin_lock_init(&drm->tile.lock);
316 348
349 nouveau_get_hdmi_dev(dev);
350
317 /* make sure AGP controller is in a consistent state before we 351 /* make sure AGP controller is in a consistent state before we
318 * (possibly) execute vbios init tables (see nouveau_agp.h) 352 * (possibly) execute vbios init tables (see nouveau_agp.h)
319 */ 353 */
@@ -388,6 +422,15 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
388 422
389 nouveau_accel_init(drm); 423 nouveau_accel_init(drm);
390 nouveau_fbcon_init(dev); 424 nouveau_fbcon_init(dev);
425
426 if (nouveau_runtime_pm != 0) {
427 pm_runtime_use_autosuspend(dev->dev);
428 pm_runtime_set_autosuspend_delay(dev->dev, 5000);
429 pm_runtime_set_active(dev->dev);
430 pm_runtime_allow(dev->dev);
431 pm_runtime_mark_last_busy(dev->dev);
432 pm_runtime_put(dev->dev);
433 }
391 return 0; 434 return 0;
392 435
393fail_dispinit: 436fail_dispinit:
@@ -409,6 +452,7 @@ nouveau_drm_unload(struct drm_device *dev)
409{ 452{
410 struct nouveau_drm *drm = nouveau_drm(dev); 453 struct nouveau_drm *drm = nouveau_drm(dev);
411 454
455 pm_runtime_get_sync(dev->dev);
412 nouveau_fbcon_fini(dev); 456 nouveau_fbcon_fini(dev);
413 nouveau_accel_fini(drm); 457 nouveau_accel_fini(drm);
414 458
@@ -424,6 +468,8 @@ nouveau_drm_unload(struct drm_device *dev)
424 nouveau_agp_fini(drm); 468 nouveau_agp_fini(drm);
425 nouveau_vga_fini(drm); 469 nouveau_vga_fini(drm);
426 470
471 if (drm->hdmi_device)
472 pci_dev_put(drm->hdmi_device);
427 nouveau_cli_destroy(&drm->client); 473 nouveau_cli_destroy(&drm->client);
428 return 0; 474 return 0;
429} 475}
@@ -450,19 +496,16 @@ nouveau_do_suspend(struct drm_device *dev)
450 int ret; 496 int ret;
451 497
452 if (dev->mode_config.num_crtc) { 498 if (dev->mode_config.num_crtc) {
453 NV_INFO(drm, "suspending fbcon...\n"); 499 NV_SUSPEND(drm, "suspending display...\n");
454 nouveau_fbcon_set_suspend(dev, 1);
455
456 NV_INFO(drm, "suspending display...\n");
457 ret = nouveau_display_suspend(dev); 500 ret = nouveau_display_suspend(dev);
458 if (ret) 501 if (ret)
459 return ret; 502 return ret;
460 } 503 }
461 504
462 NV_INFO(drm, "evicting buffers...\n"); 505 NV_SUSPEND(drm, "evicting buffers...\n");
463 ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM); 506 ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
464 507
465 NV_INFO(drm, "waiting for kernel channels to go idle...\n"); 508 NV_SUSPEND(drm, "waiting for kernel channels to go idle...\n");
466 if (drm->cechan) { 509 if (drm->cechan) {
467 ret = nouveau_channel_idle(drm->cechan); 510 ret = nouveau_channel_idle(drm->cechan);
468 if (ret) 511 if (ret)
@@ -475,7 +518,7 @@ nouveau_do_suspend(struct drm_device *dev)
475 return ret; 518 return ret;
476 } 519 }
477 520
478 NV_INFO(drm, "suspending client object trees...\n"); 521 NV_SUSPEND(drm, "suspending client object trees...\n");
479 if (drm->fence && nouveau_fence(drm)->suspend) { 522 if (drm->fence && nouveau_fence(drm)->suspend) {
480 if (!nouveau_fence(drm)->suspend(drm)) 523 if (!nouveau_fence(drm)->suspend(drm))
481 return -ENOMEM; 524 return -ENOMEM;
@@ -487,7 +530,7 @@ nouveau_do_suspend(struct drm_device *dev)
487 goto fail_client; 530 goto fail_client;
488 } 531 }
489 532
490 NV_INFO(drm, "suspending kernel object tree...\n"); 533 NV_SUSPEND(drm, "suspending kernel object tree...\n");
491 ret = nouveau_client_fini(&drm->client.base, true); 534 ret = nouveau_client_fini(&drm->client.base, true);
492 if (ret) 535 if (ret)
493 goto fail_client; 536 goto fail_client;
@@ -501,7 +544,7 @@ fail_client:
501 } 544 }
502 545
503 if (dev->mode_config.num_crtc) { 546 if (dev->mode_config.num_crtc) {
504 NV_INFO(drm, "resuming display...\n"); 547 NV_SUSPEND(drm, "resuming display...\n");
505 nouveau_display_resume(dev); 548 nouveau_display_resume(dev);
506 } 549 }
507 return ret; 550 return ret;
@@ -513,9 +556,14 @@ int nouveau_pmops_suspend(struct device *dev)
513 struct drm_device *drm_dev = pci_get_drvdata(pdev); 556 struct drm_device *drm_dev = pci_get_drvdata(pdev);
514 int ret; 557 int ret;
515 558
516 if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) 559 if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
560 drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
517 return 0; 561 return 0;
518 562
563 if (drm_dev->mode_config.num_crtc)
564 nouveau_fbcon_set_suspend(drm_dev, 1);
565
566 nv_suspend_set_printk_level(NV_DBG_INFO);
519 ret = nouveau_do_suspend(drm_dev); 567 ret = nouveau_do_suspend(drm_dev);
520 if (ret) 568 if (ret)
521 return ret; 569 return ret;
@@ -523,6 +571,7 @@ int nouveau_pmops_suspend(struct device *dev)
523 pci_save_state(pdev); 571 pci_save_state(pdev);
524 pci_disable_device(pdev); 572 pci_disable_device(pdev);
525 pci_set_power_state(pdev, PCI_D3hot); 573 pci_set_power_state(pdev, PCI_D3hot);
574 nv_suspend_set_printk_level(NV_DBG_DEBUG);
526 575
527 return 0; 576 return 0;
528} 577}
@@ -533,15 +582,15 @@ nouveau_do_resume(struct drm_device *dev)
533 struct nouveau_drm *drm = nouveau_drm(dev); 582 struct nouveau_drm *drm = nouveau_drm(dev);
534 struct nouveau_cli *cli; 583 struct nouveau_cli *cli;
535 584
536 NV_INFO(drm, "re-enabling device...\n"); 585 NV_SUSPEND(drm, "re-enabling device...\n");
537 586
538 nouveau_agp_reset(drm); 587 nouveau_agp_reset(drm);
539 588
540 NV_INFO(drm, "resuming kernel object tree...\n"); 589 NV_SUSPEND(drm, "resuming kernel object tree...\n");
541 nouveau_client_init(&drm->client.base); 590 nouveau_client_init(&drm->client.base);
542 nouveau_agp_init(drm); 591 nouveau_agp_init(drm);
543 592
544 NV_INFO(drm, "resuming client object trees...\n"); 593 NV_SUSPEND(drm, "resuming client object trees...\n");
545 if (drm->fence && nouveau_fence(drm)->resume) 594 if (drm->fence && nouveau_fence(drm)->resume)
546 nouveau_fence(drm)->resume(drm); 595 nouveau_fence(drm)->resume(drm);
547 596
@@ -553,9 +602,10 @@ nouveau_do_resume(struct drm_device *dev)
553 nouveau_pm_resume(dev); 602 nouveau_pm_resume(dev);
554 603
555 if (dev->mode_config.num_crtc) { 604 if (dev->mode_config.num_crtc) {
556 NV_INFO(drm, "resuming display...\n"); 605 NV_SUSPEND(drm, "resuming display...\n");
557 nouveau_display_resume(dev); 606 nouveau_display_repin(dev);
558 } 607 }
608
559 return 0; 609 return 0;
560} 610}
561 611
@@ -565,7 +615,8 @@ int nouveau_pmops_resume(struct device *dev)
565 struct drm_device *drm_dev = pci_get_drvdata(pdev); 615 struct drm_device *drm_dev = pci_get_drvdata(pdev);
566 int ret; 616 int ret;
567 617
568 if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) 618 if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
619 drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
569 return 0; 620 return 0;
570 621
571 pci_set_power_state(pdev, PCI_D0); 622 pci_set_power_state(pdev, PCI_D0);
@@ -575,23 +626,54 @@ int nouveau_pmops_resume(struct device *dev)
575 return ret; 626 return ret;
576 pci_set_master(pdev); 627 pci_set_master(pdev);
577 628
578 return nouveau_do_resume(drm_dev); 629 nv_suspend_set_printk_level(NV_DBG_INFO);
630 ret = nouveau_do_resume(drm_dev);
631 if (ret) {
632 nv_suspend_set_printk_level(NV_DBG_DEBUG);
633 return ret;
634 }
635 if (drm_dev->mode_config.num_crtc)
636 nouveau_fbcon_set_suspend(drm_dev, 0);
637
638 nouveau_fbcon_zfill_all(drm_dev);
639 nouveau_display_resume(drm_dev);
640 nv_suspend_set_printk_level(NV_DBG_DEBUG);
641 return 0;
579} 642}
580 643
581static int nouveau_pmops_freeze(struct device *dev) 644static int nouveau_pmops_freeze(struct device *dev)
582{ 645{
583 struct pci_dev *pdev = to_pci_dev(dev); 646 struct pci_dev *pdev = to_pci_dev(dev);
584 struct drm_device *drm_dev = pci_get_drvdata(pdev); 647 struct drm_device *drm_dev = pci_get_drvdata(pdev);
648 int ret;
649
650 nv_suspend_set_printk_level(NV_DBG_INFO);
651 if (drm_dev->mode_config.num_crtc)
652 nouveau_fbcon_set_suspend(drm_dev, 1);
585 653
586 return nouveau_do_suspend(drm_dev); 654 ret = nouveau_do_suspend(drm_dev);
655 nv_suspend_set_printk_level(NV_DBG_DEBUG);
656 return ret;
587} 657}
588 658
589static int nouveau_pmops_thaw(struct device *dev) 659static int nouveau_pmops_thaw(struct device *dev)
590{ 660{
591 struct pci_dev *pdev = to_pci_dev(dev); 661 struct pci_dev *pdev = to_pci_dev(dev);
592 struct drm_device *drm_dev = pci_get_drvdata(pdev); 662 struct drm_device *drm_dev = pci_get_drvdata(pdev);
663 int ret;
593 664
594 return nouveau_do_resume(drm_dev); 665 nv_suspend_set_printk_level(NV_DBG_INFO);
666 ret = nouveau_do_resume(drm_dev);
667 if (ret) {
668 nv_suspend_set_printk_level(NV_DBG_DEBUG);
669 return ret;
670 }
671 if (drm_dev->mode_config.num_crtc)
672 nouveau_fbcon_set_suspend(drm_dev, 0);
673 nouveau_fbcon_zfill_all(drm_dev);
674 nouveau_display_resume(drm_dev);
675 nv_suspend_set_printk_level(NV_DBG_DEBUG);
676 return 0;
595} 677}
596 678
597 679
@@ -604,19 +686,24 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
604 char name[32], tmpname[TASK_COMM_LEN]; 686 char name[32], tmpname[TASK_COMM_LEN];
605 int ret; 687 int ret;
606 688
689 /* need to bring up power immediately if opening device */
690 ret = pm_runtime_get_sync(dev->dev);
691 if (ret < 0)
692 return ret;
693
607 get_task_comm(tmpname, current); 694 get_task_comm(tmpname, current);
608 snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); 695 snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
609 696
610 ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli); 697 ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
611 if (ret) 698 if (ret)
612 return ret; 699 goto out_suspend;
613 700
614 if (nv_device(drm->device)->card_type >= NV_50) { 701 if (nv_device(drm->device)->card_type >= NV_50) {
615 ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40), 702 ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
616 0x1000, &cli->base.vm); 703 0x1000, &cli->base.vm);
617 if (ret) { 704 if (ret) {
618 nouveau_cli_destroy(cli); 705 nouveau_cli_destroy(cli);
619 return ret; 706 goto out_suspend;
620 } 707 }
621 } 708 }
622 709
@@ -625,7 +712,12 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
625 mutex_lock(&drm->client.mutex); 712 mutex_lock(&drm->client.mutex);
626 list_add(&cli->head, &drm->clients); 713 list_add(&cli->head, &drm->clients);
627 mutex_unlock(&drm->client.mutex); 714 mutex_unlock(&drm->client.mutex);
628 return 0; 715
716out_suspend:
717 pm_runtime_mark_last_busy(dev->dev);
718 pm_runtime_put_autosuspend(dev->dev);
719
720 return ret;
629} 721}
630 722
631static void 723static void
@@ -634,12 +726,15 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
634 struct nouveau_cli *cli = nouveau_cli(fpriv); 726 struct nouveau_cli *cli = nouveau_cli(fpriv);
635 struct nouveau_drm *drm = nouveau_drm(dev); 727 struct nouveau_drm *drm = nouveau_drm(dev);
636 728
729 pm_runtime_get_sync(dev->dev);
730
637 if (cli->abi16) 731 if (cli->abi16)
638 nouveau_abi16_fini(cli->abi16); 732 nouveau_abi16_fini(cli->abi16);
639 733
640 mutex_lock(&drm->client.mutex); 734 mutex_lock(&drm->client.mutex);
641 list_del(&cli->head); 735 list_del(&cli->head);
642 mutex_unlock(&drm->client.mutex); 736 mutex_unlock(&drm->client.mutex);
737
643} 738}
644 739
645static void 740static void
@@ -647,6 +742,8 @@ nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
647{ 742{
648 struct nouveau_cli *cli = nouveau_cli(fpriv); 743 struct nouveau_cli *cli = nouveau_cli(fpriv);
649 nouveau_cli_destroy(cli); 744 nouveau_cli_destroy(cli);
745 pm_runtime_mark_last_busy(dev->dev);
746 pm_runtime_put_autosuspend(dev->dev);
650} 747}
651 748
652static const struct drm_ioctl_desc 749static const struct drm_ioctl_desc
@@ -665,12 +762,30 @@ nouveau_ioctls[] = {
665 DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH), 762 DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
666}; 763};
667 764
765long nouveau_drm_ioctl(struct file *filp,
766 unsigned int cmd, unsigned long arg)
767{
768 struct drm_file *file_priv = filp->private_data;
769 struct drm_device *dev;
770 long ret;
771 dev = file_priv->minor->dev;
772
773 ret = pm_runtime_get_sync(dev->dev);
774 if (ret < 0)
775 return ret;
776
777 ret = drm_ioctl(filp, cmd, arg);
778
779 pm_runtime_mark_last_busy(dev->dev);
780 pm_runtime_put_autosuspend(dev->dev);
781 return ret;
782}
668static const struct file_operations 783static const struct file_operations
669nouveau_driver_fops = { 784nouveau_driver_fops = {
670 .owner = THIS_MODULE, 785 .owner = THIS_MODULE,
671 .open = drm_open, 786 .open = drm_open,
672 .release = drm_release, 787 .release = drm_release,
673 .unlocked_ioctl = drm_ioctl, 788 .unlocked_ioctl = nouveau_drm_ioctl,
674 .mmap = nouveau_ttm_mmap, 789 .mmap = nouveau_ttm_mmap,
675 .poll = drm_poll, 790 .poll = drm_poll,
676 .read = drm_read, 791 .read = drm_read,
@@ -753,6 +868,90 @@ nouveau_drm_pci_table[] = {
753 {} 868 {}
754}; 869};
755 870
871static int nouveau_pmops_runtime_suspend(struct device *dev)
872{
873 struct pci_dev *pdev = to_pci_dev(dev);
874 struct drm_device *drm_dev = pci_get_drvdata(pdev);
875 int ret;
876
877 if (nouveau_runtime_pm == 0)
878 return -EINVAL;
879
880 drm_kms_helper_poll_disable(drm_dev);
881 vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
882 nouveau_switcheroo_optimus_dsm();
883 ret = nouveau_do_suspend(drm_dev);
884 pci_save_state(pdev);
885 pci_disable_device(pdev);
886 pci_set_power_state(pdev, PCI_D3cold);
887 drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
888 return ret;
889}
890
891static int nouveau_pmops_runtime_resume(struct device *dev)
892{
893 struct pci_dev *pdev = to_pci_dev(dev);
894 struct drm_device *drm_dev = pci_get_drvdata(pdev);
895 struct nouveau_device *device = nouveau_dev(drm_dev);
896 int ret;
897
898 if (nouveau_runtime_pm == 0)
899 return -EINVAL;
900
901 pci_set_power_state(pdev, PCI_D0);
902 pci_restore_state(pdev);
903 ret = pci_enable_device(pdev);
904 if (ret)
905 return ret;
906 pci_set_master(pdev);
907
908 ret = nouveau_do_resume(drm_dev);
909 nouveau_display_resume(drm_dev);
910 drm_kms_helper_poll_enable(drm_dev);
911 /* do magic */
912 nv_mask(device, 0x88488, (1 << 25), (1 << 25));
913 vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
914 drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
915 return ret;
916}
917
918static int nouveau_pmops_runtime_idle(struct device *dev)
919{
920 struct pci_dev *pdev = to_pci_dev(dev);
921 struct drm_device *drm_dev = pci_get_drvdata(pdev);
922 struct nouveau_drm *drm = nouveau_drm(drm_dev);
923 struct drm_crtc *crtc;
924
925 if (nouveau_runtime_pm == 0)
926 return -EBUSY;
927
928 /* are we optimus enabled? */
929 if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
930 DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
931 return -EBUSY;
932 }
933
934 /* if we have a hdmi audio device - make sure it has a driver loaded */
935 if (drm->hdmi_device) {
936 if (!drm->hdmi_device->driver) {
937 DRM_DEBUG_DRIVER("failing to power off - no HDMI audio driver loaded\n");
938 pm_runtime_mark_last_busy(dev);
939 return -EBUSY;
940 }
941 }
942
943 list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) {
944 if (crtc->enabled) {
945 DRM_DEBUG_DRIVER("failing to power off - crtc active\n");
946 return -EBUSY;
947 }
948 }
949 pm_runtime_mark_last_busy(dev);
950 pm_runtime_autosuspend(dev);
951 /* we don't want the main rpm_idle to call suspend - we want to autosuspend */
952 return 1;
953}
954
756static const struct dev_pm_ops nouveau_pm_ops = { 955static const struct dev_pm_ops nouveau_pm_ops = {
757 .suspend = nouveau_pmops_suspend, 956 .suspend = nouveau_pmops_suspend,
758 .resume = nouveau_pmops_resume, 957 .resume = nouveau_pmops_resume,
@@ -760,6 +959,9 @@ static const struct dev_pm_ops nouveau_pm_ops = {
760 .thaw = nouveau_pmops_thaw, 959 .thaw = nouveau_pmops_thaw,
761 .poweroff = nouveau_pmops_freeze, 960 .poweroff = nouveau_pmops_freeze,
762 .restore = nouveau_pmops_resume, 961 .restore = nouveau_pmops_resume,
962 .runtime_suspend = nouveau_pmops_runtime_suspend,
963 .runtime_resume = nouveau_pmops_runtime_resume,
964 .runtime_idle = nouveau_pmops_runtime_idle,
763}; 965};
764 966
765static struct pci_driver 967static struct pci_driver
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 41ff7e0d403a..994fd6ec373b 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)
70 return fpriv ? fpriv->driver_priv : NULL; 70 return fpriv ? fpriv->driver_priv : NULL;
71} 71}
72 72
73extern int nouveau_runtime_pm;
74
73struct nouveau_drm { 75struct nouveau_drm {
74 struct nouveau_cli client; 76 struct nouveau_cli client;
75 struct drm_device *dev; 77 struct drm_device *dev;
@@ -129,6 +131,12 @@ struct nouveau_drm {
129 131
130 /* power management */ 132 /* power management */
131 struct nouveau_pm *pm; 133 struct nouveau_pm *pm;
134
135 /* display power reference */
136 bool have_disp_power_ref;
137
138 struct dev_pm_domain vga_pm_domain;
139 struct pci_dev *hdmi_device;
132}; 140};
133 141
134static inline struct nouveau_drm * 142static inline struct nouveau_drm *
@@ -146,6 +154,7 @@ nouveau_dev(struct drm_device *dev)
146int nouveau_pmops_suspend(struct device *); 154int nouveau_pmops_suspend(struct device *);
147int nouveau_pmops_resume(struct device *); 155int nouveau_pmops_resume(struct device *);
148 156
157#define NV_SUSPEND(cli, fmt, args...) nv_suspend((cli), fmt, ##args)
149#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args) 158#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
150#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args) 159#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
151#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args) 160#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
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,
32{ 32{
33 struct drm_device *dev = pci_get_drvdata(pdev); 33 struct drm_device *dev = pci_get_drvdata(pdev);
34 34
35 if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF)
36 return;
37
35 if (state == VGA_SWITCHEROO_ON) { 38 if (state == VGA_SWITCHEROO_ON) {
36 printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); 39 printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
37 dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 40 dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
@@ -78,8 +81,17 @@ void
78nouveau_vga_init(struct nouveau_drm *drm) 81nouveau_vga_init(struct nouveau_drm *drm)
79{ 82{
80 struct drm_device *dev = drm->dev; 83 struct drm_device *dev = drm->dev;
84 bool runtime = false;
81 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 85 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
82 vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false); 86
87 if (nouveau_runtime_pm == 1)
88 runtime = true;
89 if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
90 runtime = true;
91 vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime);
92
93 if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
94 vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain);
83} 95}
84 96
85void 97void
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 8b40a36c1b57..9d2092a5ed38 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1326,7 +1326,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
1326 .cursor_set = nv50_crtc_cursor_set, 1326 .cursor_set = nv50_crtc_cursor_set,
1327 .cursor_move = nv50_crtc_cursor_move, 1327 .cursor_move = nv50_crtc_cursor_move,
1328 .gamma_set = nv50_crtc_gamma_set, 1328 .gamma_set = nv50_crtc_gamma_set,
1329 .set_config = drm_crtc_helper_set_config, 1329 .set_config = nouveau_crtc_set_config,
1330 .destroy = nv50_crtc_destroy, 1330 .destroy = nv50_crtc_destroy,
1331 .page_flip = nouveau_crtc_page_flip, 1331 .page_flip = nouveau_crtc_page_flip,
1332}; 1332};