diff options
author | Dave Airlie <airlied@redhat.com> | 2012-09-10 00:20:51 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-08-28 23:30:46 -0400 |
commit | 5addcf0a5f0fadceba6bd562d0616a1c5d4c1a4d (patch) | |
tree | 5162dd3bf9dd94b2b59b01a320fc019deda052bd | |
parent | 13bb9cc8726c716a7f271fc2c760ba15e1fdd38c (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.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/core/printk.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/init.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/mc/base.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/dispnv04/crtc.c | 49 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 42 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 250 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_vga.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 2 |
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 | ||
30 | int nv_printk_suspend_level = NV_DBG_DEBUG; | ||
31 | |||
30 | void | 32 | void |
31 | nv_printk_(struct nouveau_object *object, const char *pfx, int level, | 33 | nv_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 | |||
80 | const 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 | ||
18 | extern 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 | |||
23 | const char *nv_printk_level_to_pfx(int level); | ||
18 | void __printf(4, 5) | 24 | void __printf(4, 5) |
19 | nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); | 25 | nv_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 | |||
42 | static 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 | ||
27 | static irqreturn_t | 28 | static irqreturn_t |
28 | nouveau_mc_intr(int irq, void *arg) | 29 | nouveau_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 | ||
1011 | int | ||
1012 | nouveau_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 | |||
1010 | static const struct drm_crtc_funcs nv04_crtc_funcs = { | 1057 | static 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 | ||
31 | static struct nouveau_dsm_priv { | 50 | static 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 | ||
335 | void nouveau_unregister_dsm_handler(void) | 367 | void 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 | ||
302 | detect_analog: | 313 | detect_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 | ||
322 | static enum drm_connector_status | 339 | static 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 | ||
418 | void | 418 | void |
419 | nouveau_display_resume(struct drm_device *dev) | 419 | nouveau_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); | 446 | void |
446 | nouveau_fbcon_zfill_all(dev); | 447 | nouveau_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); | |||
57 | int nouveau_display_init(struct drm_device *dev); | 57 | int nouveau_display_init(struct drm_device *dev); |
58 | void nouveau_display_fini(struct drm_device *dev); | 58 | void nouveau_display_fini(struct drm_device *dev); |
59 | int nouveau_display_suspend(struct drm_device *dev); | 59 | int nouveau_display_suspend(struct drm_device *dev); |
60 | void nouveau_display_repin(struct drm_device *dev); | ||
60 | void nouveau_display_resume(struct drm_device *dev); | 61 | void nouveau_display_resume(struct drm_device *dev); |
61 | 62 | ||
62 | int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | 63 | int 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 | ||
72 | void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); | 73 | void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); |
73 | 74 | ||
75 | int nouveau_crtc_set_config(struct drm_mode_set *set); | ||
74 | #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT | 76 | #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT |
75 | extern int nouveau_backlight_init(struct drm_device *); | 77 | extern int nouveau_backlight_init(struct drm_device *); |
76 | extern void nouveau_backlight_exit(struct drm_device *); | 78 | 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 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, " | |||
69 | int nouveau_modeset = -1; | 72 | int nouveau_modeset = -1; |
70 | module_param_named(modeset, nouveau_modeset, int, 0400); | 73 | module_param_named(modeset, nouveau_modeset, int, 0400); |
71 | 74 | ||
75 | MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); | ||
76 | int nouveau_runtime_pm = -1; | ||
77 | module_param_named(runpm, nouveau_runtime_pm, int, 0400); | ||
78 | |||
72 | static struct drm_driver driver; | 79 | static struct drm_driver driver; |
73 | 80 | ||
74 | static int | 81 | static 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 | |||
308 | static void | ||
309 | nouveau_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 | |||
299 | static int | 331 | static int |
300 | nouveau_drm_load(struct drm_device *dev, unsigned long flags) | 332 | nouveau_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 | ||
393 | fail_dispinit: | 436 | fail_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 | ||
581 | static int nouveau_pmops_freeze(struct device *dev) | 644 | static 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 | ||
589 | static int nouveau_pmops_thaw(struct device *dev) | 659 | static 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 | |
716 | out_suspend: | ||
717 | pm_runtime_mark_last_busy(dev->dev); | ||
718 | pm_runtime_put_autosuspend(dev->dev); | ||
719 | |||
720 | return ret; | ||
629 | } | 721 | } |
630 | 722 | ||
631 | static void | 723 | static 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 | ||
645 | static void | 740 | static 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 | ||
652 | static const struct drm_ioctl_desc | 749 | static 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 | ||
765 | long 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 | } | ||
668 | static const struct file_operations | 783 | static const struct file_operations |
669 | nouveau_driver_fops = { | 784 | nouveau_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 | ||
871 | static 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 | |||
891 | static 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 | |||
918 | static 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 | |||
756 | static const struct dev_pm_ops nouveau_pm_ops = { | 955 | static 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 | ||
765 | static struct pci_driver | 967 | static 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 | ||
73 | extern int nouveau_runtime_pm; | ||
74 | |||
73 | struct nouveau_drm { | 75 | struct 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 | ||
134 | static inline struct nouveau_drm * | 142 | static inline struct nouveau_drm * |
@@ -146,6 +154,7 @@ nouveau_dev(struct drm_device *dev) | |||
146 | int nouveau_pmops_suspend(struct device *); | 154 | int nouveau_pmops_suspend(struct device *); |
147 | int nouveau_pmops_resume(struct device *); | 155 | int 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 | |||
78 | nouveau_vga_init(struct nouveau_drm *drm) | 81 | nouveau_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 | ||
85 | void | 97 | void |
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 | }; |