* [PATCH 0/2] Add runtime PM support to radeon
@ 2013-09-20 18:18 Alex Deucher
2013-09-20 18:18 ` [PATCH 1/2] drm/radeon: convert to pmops Alex Deucher
2013-09-20 18:18 ` [PATCH 2/2] drm/radeon: add runtime PM support (v2) Alex Deucher
0 siblings, 2 replies; 10+ messages in thread
From: Alex Deucher @ 2013-09-20 18:18 UTC (permalink / raw)
To: dri-devel, airlied; +Cc: Alex Deucher
This is a minor cleanup of Dave's initial patches to
add runtime PM to radeon powerxpress laptops. It
seems to work well on PX laptop I have access to at
the moment. This patch set requires kernel 3.12.
Dave Airlie (2):
drm/radeon: convert to pmops
drm/radeon: add runtime PM support (v2)
drivers/gpu/drm/radeon/radeon.h | 8 +-
drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
drivers/gpu/drm/radeon/radeon_connectors.c | 63 +++++++++--
drivers/gpu/drm/radeon/radeon_device.c | 69 ++++++++----
drivers/gpu/drm/radeon/radeon_display.c | 47 +++++++-
drivers/gpu/drm/radeon/radeon_drv.c | 158 ++++++++++++++++++++++++---
drivers/gpu/drm/radeon/radeon_drv.h | 3 +
drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
drivers/gpu/drm/radeon/radeon_kms.c | 26 ++++-
10 files changed, 335 insertions(+), 53 deletions(-)
--
1.8.3.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] drm/radeon: convert to pmops
2013-09-20 18:18 [PATCH 0/2] Add runtime PM support to radeon Alex Deucher
@ 2013-09-20 18:18 ` Alex Deucher
2013-09-20 18:18 ` [PATCH 2/2] drm/radeon: add runtime PM support (v2) Alex Deucher
1 sibling, 0 replies; 10+ messages in thread
From: Alex Deucher @ 2013-09-20 18:18 UTC (permalink / raw)
To: dri-devel, airlied; +Cc: Alex Deucher, Dave Airlie
From: Dave Airlie <airlied@redhat.com>
This is a pre-requisite for runtime pm on powerxpress systems.
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---
drivers/gpu/drm/radeon/radeon.h | 4 +--
drivers/gpu/drm/radeon/radeon_device.c | 27 +++++++++----------
drivers/gpu/drm/radeon/radeon_drv.c | 48 ++++++++++++++++++++++++----------
3 files changed, 49 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index a400ac1..986100a 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -2673,8 +2673,8 @@ extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
-extern int radeon_resume_kms(struct drm_device *dev);
-extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
+extern int radeon_resume_kms(struct drm_device *dev, bool resume);
+extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size);
extern void radeon_program_register_sequence(struct radeon_device *rdev,
const u32 *registers,
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index e29faa7..37cfcee 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1076,7 +1076,6 @@ static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
if (state == VGA_SWITCHEROO_ON) {
unsigned d3_delay = dev->pdev->d3_delay;
@@ -1087,7 +1086,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev))
dev->pdev->d3_delay = 20;
- radeon_resume_kms(dev);
+ radeon_resume_kms(dev, 1);
dev->pdev->d3_delay = d3_delay;
@@ -1097,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
printk(KERN_INFO "radeon: switched off\n");
drm_kms_helper_poll_disable(dev);
dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
- radeon_suspend_kms(dev, pmm);
+ radeon_suspend_kms(dev, 1);
dev->switch_power_state = DRM_SWITCH_POWER_OFF;
}
}
@@ -1374,7 +1373,7 @@ void radeon_device_fini(struct radeon_device *rdev)
* Returns 0 for success or an error on failure.
* Called at driver suspend.
*/
-int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
+int radeon_suspend_kms(struct drm_device *dev, bool suspend)
{
struct radeon_device *rdev;
struct drm_crtc *crtc;
@@ -1385,9 +1384,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
if (dev == NULL || dev->dev_private == NULL) {
return -ENODEV;
}
- if (state.event == PM_EVENT_PRETHAW) {
- return 0;
- }
+
rdev = dev->dev_private;
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
@@ -1446,7 +1443,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
radeon_agp_suspend(rdev);
pci_save_state(dev->pdev);
- if (state.event == PM_EVENT_SUSPEND) {
+ if (suspend) {
/* Shut down the device */
pci_disable_device(dev->pdev);
pci_set_power_state(dev->pdev, PCI_D3hot);
@@ -1466,7 +1463,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
* Returns 0 for success or an error on failure.
* Called at driver resume.
*/
-int radeon_resume_kms(struct drm_device *dev)
+int radeon_resume_kms(struct drm_device *dev, bool resume)
{
struct drm_connector *connector;
struct radeon_device *rdev = dev->dev_private;
@@ -1476,11 +1473,13 @@ int radeon_resume_kms(struct drm_device *dev)
return 0;
console_lock();
- pci_set_power_state(dev->pdev, PCI_D0);
- pci_restore_state(dev->pdev);
- if (pci_enable_device(dev->pdev)) {
- console_unlock();
- return -1;
+ if (resume) {
+ pci_set_power_state(dev->pdev, PCI_D0);
+ pci_restore_state(dev->pdev);
+ if (pci_enable_device(dev->pdev)) {
+ console_unlock();
+ return -1;
+ }
}
/* resume AGP if in use */
radeon_agp_resume(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index cdd12dc..788bfb0 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -87,8 +87,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
struct drm_file *file_priv);
void radeon_driver_preclose_kms(struct drm_device *dev,
struct drm_file *file_priv);
-int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
-int radeon_resume_kms(struct drm_device *dev);
+int radeon_suspend_kms(struct drm_device *dev, bool suspend);
+int radeon_resume_kms(struct drm_device *dev, bool resume);
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
@@ -353,20 +353,43 @@ radeon_pci_remove(struct pci_dev *pdev)
drm_put_dev(dev);
}
-static int
-radeon_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int radeon_pmops_suspend(struct device *dev)
{
- struct drm_device *dev = pci_get_drvdata(pdev);
- return radeon_suspend_kms(dev, state);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ return radeon_suspend_kms(drm_dev, 1);
}
-static int
-radeon_pci_resume(struct pci_dev *pdev)
+static int radeon_pmops_resume(struct device *dev)
{
- struct drm_device *dev = pci_get_drvdata(pdev);
- return radeon_resume_kms(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ return radeon_resume_kms(drm_dev, 1);
+}
+
+static int radeon_pmops_freeze(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ return radeon_suspend_kms(drm_dev, 0);
}
+static int radeon_pmops_thaw(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ return radeon_resume_kms(drm_dev, 0);
+}
+
+static const struct dev_pm_ops radeon_pm_ops = {
+ .suspend = radeon_pmops_suspend,
+ .resume = radeon_pmops_resume,
+ .freeze = radeon_pmops_freeze,
+ .thaw = radeon_pmops_thaw,
+ .poweroff = radeon_pmops_freeze,
+ .restore = radeon_pmops_resume,
+};
+
static const struct file_operations radeon_driver_kms_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -392,8 +415,6 @@ static struct drm_driver kms_driver = {
.postclose = radeon_driver_postclose_kms,
.lastclose = radeon_driver_lastclose_kms,
.unload = radeon_driver_unload_kms,
- .suspend = radeon_suspend_kms,
- .resume = radeon_resume_kms,
.get_vblank_counter = radeon_get_vblank_counter_kms,
.enable_vblank = radeon_enable_vblank_kms,
.disable_vblank = radeon_disable_vblank_kms,
@@ -451,8 +472,7 @@ static struct pci_driver radeon_kms_pci_driver = {
.id_table = pciidlist,
.probe = radeon_pci_probe,
.remove = radeon_pci_remove,
- .suspend = radeon_pci_suspend,
- .resume = radeon_pci_resume,
+ .driver.pm = &radeon_pm_ops,
};
static int __init radeon_init(void)
--
1.8.3.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 18:18 [PATCH 0/2] Add runtime PM support to radeon Alex Deucher
2013-09-20 18:18 ` [PATCH 1/2] drm/radeon: convert to pmops Alex Deucher
@ 2013-09-20 18:18 ` Alex Deucher
2013-09-20 20:25 ` Mike Lothian
1 sibling, 1 reply; 10+ messages in thread
From: Alex Deucher @ 2013-09-20 18:18 UTC (permalink / raw)
To: dri-devel, airlied; +Cc: Alex Deucher, Dave Airlie
From: Dave Airlie <airlied@redhat.com>
This hooks radeon up to the runtime PM system to enable
dynamic power management for secondary GPUs in switchable
and powerxpress laptops.
v2: agd5f: clean up, add module parameter
Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---
drivers/gpu/drm/radeon/radeon.h | 8 +-
drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
drivers/gpu/drm/radeon/radeon_drv.c | 122 +++++++++++++++++++++++++--
drivers/gpu/drm/radeon/radeon_drv.h | 3 +
drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
10 files changed, 299 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 986100a..ad54525 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
extern int radeon_fastfb;
extern int radeon_dpm;
extern int radeon_aspm;
+extern int radeon_runtime_pm;
/*
* Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -2212,6 +2213,9 @@ struct radeon_device {
/* clock, powergating flags */
u32 cg_flags;
u32 pg_flags;
+
+ struct dev_pm_domain vga_pm_domain;
+ bool have_disp_power_ref;
};
int radeon_device_init(struct radeon_device *rdev,
@@ -2673,8 +2677,8 @@ extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
-extern int radeon_resume_kms(struct drm_device *dev, bool resume);
-extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
+extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
+extern int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size);
extern void radeon_program_register_sequence(struct radeon_device *rdev,
const u32 *registers,
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index d96070b..6153ec1 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -59,6 +59,10 @@ struct atpx_mux {
u16 mux;
} __packed;
+bool radeon_is_px(void) {
+ return radeon_atpx_priv.atpx_detected;
+}
+
/**
* radeon_atpx_call - call an ATPX method
*
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 79159b5..5855b5b 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -31,6 +31,8 @@
#include "radeon.h"
#include "atom.h"
+#include <linux/pm_runtime.h>
+
extern void
radeon_combios_connected_scratch_regs(struct drm_connector *connector,
struct drm_encoder *encoder,
@@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
enum drm_connector_status ret = connector_status_disconnected;
+ int r;
+
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
if (encoder) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
/* check acpi lid status ??? */
radeon_connector_update_scratch_regs(connector, ret);
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
return ret;
}
@@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
struct drm_encoder_helper_funcs *encoder_funcs;
bool dret = false;
enum drm_connector_status ret = connector_status_disconnected;
+ int r;
+
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
encoder = radeon_best_single_encoder(connector);
if (!encoder)
@@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
* detected a monitor via load.
*/
if (radeon_connector->detected_by_load)
- return connector->status;
- else
- return ret;
+ ret = connector->status;
+ goto out;
}
if (radeon_connector->dac_load_detect && encoder) {
@@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
}
radeon_connector_update_scratch_regs(connector, ret);
+
+out:
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+
return ret;
}
@@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
struct drm_encoder_helper_funcs *encoder_funcs;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
enum drm_connector_status ret = connector_status_disconnected;
+ int r;
if (!radeon_connector->dac_load_detect)
return ret;
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+
encoder = radeon_best_single_encoder(connector);
if (!encoder)
ret = connector_status_disconnected;
@@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
if (ret == connector_status_connected)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
radeon_connector_update_scratch_regs(connector, ret);
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
return ret;
}
@@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
struct drm_encoder *encoder = NULL;
struct drm_encoder_helper_funcs *encoder_funcs;
struct drm_mode_object *obj;
- int i;
+ int i, r;
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
- if (!force && radeon_check_hpd_status_unchanged(connector))
- return connector->status;
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+
+ if (!force && radeon_check_hpd_status_unchanged(connector)) {
+ ret = connector->status;
+ goto exit;
+ }
if (radeon_connector->ddc_bus)
dret = radeon_ddc_probe(radeon_connector, false);
@@ -1110,6 +1141,11 @@ out:
/* updated in get modes as well since we need to know if it's analog or digital */
radeon_connector_update_scratch_regs(connector, ret);
+
+exit:
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+
return ret;
}
@@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
enum drm_connector_status ret = connector_status_disconnected;
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+ int r;
- if (!force && radeon_check_hpd_status_unchanged(connector))
- return connector->status;
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+
+ if (!force && radeon_check_hpd_status_unchanged(connector)) {
+ ret = connector->status;
+ goto out;
+ }
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
@@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
}
radeon_connector_update_scratch_regs(connector, ret);
+out:
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 37cfcee..b9b9dfd 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
"LAST",
};
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool radeon_is_px(void);
+#else
+static inline bool radeon_is_px(void) { return false; }
+#endif
+
/**
* radeon_program_register_sequence - program an array of registers.
*
@@ -1076,6 +1082,10 @@ static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
{
struct drm_device *dev = pci_get_drvdata(pdev);
+
+ if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
+ return;
+
if (state == VGA_SWITCHEROO_ON) {
unsigned d3_delay = dev->pdev->d3_delay;
@@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev))
dev->pdev->d3_delay = 20;
- radeon_resume_kms(dev, 1);
+ radeon_resume_kms(dev, true, true);
dev->pdev->d3_delay = d3_delay;
@@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
printk(KERN_INFO "radeon: switched off\n");
drm_kms_helper_poll_disable(dev);
dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
- radeon_suspend_kms(dev, 1);
+ radeon_suspend_kms(dev, true, true);
dev->switch_power_state = DRM_SWITCH_POWER_OFF;
}
}
@@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device *rdev,
{
int r, i;
int dma_bits;
+ bool runtime = false;
rdev->shutdown = false;
rdev->dev = &pdev->dev;
@@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device *rdev,
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
- vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, false);
+
+ if (radeon_runtime_pm == 1)
+ runtime = true;
+ if ((radeon_runtime_pm == -1) && radeon_is_px())
+ runtime = true;
+ vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
+ if (runtime)
+ vga_switcheroo_init_domain_pm_ops(rdev->dev, &rdev->vga_pm_domain);
r = radeon_init(rdev);
if (r)
@@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device *rdev)
* Returns 0 for success or an error on failure.
* Called at driver suspend.
*/
-int radeon_suspend_kms(struct drm_device *dev, bool suspend)
+int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
{
struct radeon_device *rdev;
struct drm_crtc *crtc;
@@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend)
pci_disable_device(dev->pdev);
pci_set_power_state(dev->pdev, PCI_D3hot);
}
- console_lock();
- radeon_fbdev_set_suspend(rdev, 1);
- console_unlock();
+
+ if (fbcon) {
+ console_lock();
+ radeon_fbdev_set_suspend(rdev, 1);
+ console_unlock();
+ }
return 0;
}
@@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend)
* Returns 0 for success or an error on failure.
* Called at driver resume.
*/
-int radeon_resume_kms(struct drm_device *dev, bool resume)
+int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
{
struct drm_connector *connector;
struct radeon_device *rdev = dev->dev_private;
@@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev, bool resume)
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
- console_lock();
+ if (fbcon) {
+ console_lock();
+ }
if (resume) {
pci_set_power_state(dev->pdev, PCI_D0);
pci_restore_state(dev->pdev);
if (pci_enable_device(dev->pdev)) {
- console_unlock();
+ if (fbcon)
+ console_unlock();
return -1;
}
}
@@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev, bool resume)
radeon_pm_resume(rdev);
radeon_restore_bios_scratch_regs(rdev);
- radeon_fbdev_set_suspend(rdev, 0);
- console_unlock();
-
+ if (fbcon) {
+ radeon_fbdev_set_suspend(rdev, 0);
+ console_unlock();
+ }
+
/* init dig PHYs, disp eng pll */
if (rdev->is_atom_bios) {
radeon_atom_encoder_init(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 0d1aa05..bc37e33 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -30,6 +30,7 @@
#include "atom.h"
#include <asm/div64.h>
+#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
@@ -494,11 +495,55 @@ unlock_free:
return r;
}
+static int
+radeon_crtc_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *dev;
+ struct radeon_device *rdev;
+ struct drm_crtc *crtc;
+ bool active = false;
+ int ret;
+
+ if (!set || !set->crtc)
+ return -EINVAL;
+
+ dev = set->crtc->dev;
+
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = drm_crtc_helper_set_config(set);
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ if (crtc->enabled)
+ active = true;
+
+ pm_runtime_mark_last_busy(dev->dev);
+
+ rdev = dev->dev_private;
+ /* if we have active crtcs and we don't have a power ref,
+ take the current one */
+ if (active && !rdev->have_disp_power_ref) {
+ rdev->have_disp_power_ref = true;
+ return ret;
+ }
+ /* if we have no active crtcs, then drop the power ref
+ we got before */
+ if (!active && rdev->have_disp_power_ref) {
+ pm_runtime_put_autosuspend(dev->dev);
+ rdev->have_disp_power_ref = false;
+ }
+
+ /* drop the power reference we got coming in here */
+ pm_runtime_put_autosuspend(dev->dev);
+ return ret;
+}
static const struct drm_crtc_funcs radeon_crtc_funcs = {
.cursor_set = radeon_crtc_cursor_set,
.cursor_move = radeon_crtc_cursor_move,
.gamma_set = radeon_crtc_gamma_set,
- .set_config = drm_crtc_helper_set_config,
+ .set_config = radeon_crtc_set_config,
.destroy = radeon_crtc_destroy,
.page_flip = radeon_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 788bfb0..427c64f 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -36,8 +36,9 @@
#include <drm/drm_pciids.h>
#include <linux/console.h>
#include <linux/module.h>
-
-
+#include <linux/pm_runtime.h>
+#include <linux/vga_switcheroo.h>
+#include "drm_crtc_helper.h"
/*
* KMS wrapper.
* - 2.0.0 - initial interface
@@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
struct drm_file *file_priv);
void radeon_driver_preclose_kms(struct drm_device *dev,
struct drm_file *file_priv);
-int radeon_suspend_kms(struct drm_device *dev, bool suspend);
-int radeon_resume_kms(struct drm_device *dev, bool resume);
+int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
+int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
@@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor *minor);
#if defined(CONFIG_VGA_SWITCHEROO)
void radeon_register_atpx_handler(void);
void radeon_unregister_atpx_handler(void);
+bool radeon_is_px(void);
#else
static inline void radeon_register_atpx_handler(void) {}
static inline void radeon_unregister_atpx_handler(void) {}
+static inline bool radeon_is_px(void) { return false; }
#endif
int radeon_no_wb;
@@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
int radeon_fastfb = 0;
int radeon_dpm = -1;
int radeon_aspm = -1;
+int radeon_runtime_pm = -1;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)");
module_param_named(aspm, radeon_aspm, int, 0444);
+MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1 = PX only default)");
+module_param_named(runpm, radeon_runtime_pm, int, 0444);
+
static struct pci_device_id pciidlist[] = {
radeon_PCI_IDS
};
@@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
return 0;
}
+
static const struct file_operations radeon_driver_old_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- return radeon_suspend_kms(drm_dev, 1);
+ return radeon_suspend_kms(drm_dev, true, true);
}
static int radeon_pmops_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- return radeon_resume_kms(drm_dev, 1);
+ return radeon_resume_kms(drm_dev, true, true);
}
static int radeon_pmops_freeze(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- return radeon_suspend_kms(drm_dev, 0);
+ return radeon_suspend_kms(drm_dev, false, true);
}
static int radeon_pmops_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- return radeon_resume_kms(drm_dev, 0);
+ return radeon_resume_kms(drm_dev, false, true);
+}
+
+static int radeon_pmops_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ int ret;
+
+ if (radeon_runtime_pm == 0)
+ return -EINVAL;
+
+ drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+ drm_kms_helper_poll_disable(drm_dev);
+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+
+ ret = radeon_suspend_kms(drm_dev, false, false);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3cold);
+ drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
+
+ return 0;
+}
+
+static int radeon_pmops_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ int ret;
+
+ if (radeon_runtime_pm == 0)
+ return -EINVAL;
+
+ drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+ pci_set_master(pdev);
+
+ ret = radeon_resume_kms(drm_dev, false, false);
+ drm_kms_helper_poll_enable(drm_dev);
+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+ drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+ return 0;
+}
+
+static int radeon_pmops_runtime_idle(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct drm_crtc *crtc;
+
+ if (radeon_runtime_pm == 0)
+ return -EBUSY;
+
+ /* are we PX enabled? */
+ if (radeon_runtime_pm == -1 && !radeon_is_px()) {
+ DRM_DEBUG_DRIVER("failing to power off - not px\n");
+ return -EBUSY;
+ }
+
+ list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+ if (crtc->enabled) {
+ DRM_DEBUG_DRIVER("failing to power off - crtc active\n");
+ return -EBUSY;
+ }
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_autosuspend(dev);
+ /* we don't want the main rpm_idle to call suspend - we want to autosuspend */
+ return 1;
+}
+
+long radeon_drm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct drm_file *file_priv = filp->private_data;
+ struct drm_device *dev;
+ long ret;
+ dev = file_priv->minor->dev;
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = drm_ioctl(filp, cmd, arg);
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+ return ret;
}
static const struct dev_pm_ops radeon_pm_ops = {
@@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops = {
.thaw = radeon_pmops_thaw,
.poweroff = radeon_pmops_freeze,
.restore = radeon_pmops_resume,
+ .runtime_suspend = radeon_pmops_runtime_suspend,
+ .runtime_resume = radeon_pmops_runtime_resume,
+ .runtime_idle = radeon_pmops_runtime_idle,
};
static const struct file_operations radeon_driver_kms_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
- .unlocked_ioctl = drm_ioctl,
+ .unlocked_ioctl = radeon_drm_ioctl,
.mmap = radeon_mmap,
.poll = drm_poll,
.read = drm_read,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index b369d42..543dcfa 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -113,6 +113,9 @@
#define DRIVER_MINOR 33
#define DRIVER_PATCHLEVEL 0
+long radeon_drm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
/* The rest of the file is DEPRECATED! */
#ifdef CONFIG_DRM_RADEON_UMS
diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c
index c180df8..bdb0f93 100644
--- a/drivers/gpu/drm/radeon/radeon_ioc32.c
+++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
@@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
- ret = drm_ioctl(filp, cmd, arg);
+ ret = radeon_drm_ioctl(filp, cmd, arg);
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index cc9e848..ec6240b 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -32,6 +32,8 @@
#include "radeon.h"
#include "atom.h"
+#include <linux/pm_runtime.h>
+
#define RADEON_WAIT_IDLE_TIMEOUT 200
/**
@@ -47,8 +49,12 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
struct radeon_device *rdev = dev->dev_private;
+ irqreturn_t ret;
- return radeon_irq_process(rdev);
+ ret = radeon_irq_process(rdev);
+ if (ret == IRQ_HANDLED)
+ pm_runtime_mark_last_busy(dev->dev);
+ return ret;
}
/*
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 61580dd..bffff51 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -32,7 +32,7 @@
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
-
+#include <linux/pm_runtime.h>
/**
* radeon_driver_unload_kms - Main unload function for KMS.
*
@@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
if (rdev == NULL)
return 0;
+
if (rdev->rmmio == NULL)
goto done_free;
+
+ pm_runtime_get_sync(dev->dev);
+
radeon_acpi_fini(rdev);
+
radeon_modeset_fini(rdev);
radeon_device_fini(rdev);
@@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
"Error during ACPI methods call\n");
}
+ if (radeon_runtime_pm != 0) {
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_autosuspend_delay(dev->dev, 5000);
+ pm_runtime_set_active(dev->dev);
+ pm_runtime_allow(dev->dev);
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+ }
+
out:
if (r)
radeon_driver_unload_kms(dev);
+
+
return r;
}
@@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device *dev)
int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
{
struct radeon_device *rdev = dev->dev_private;
+ int r;
file_priv->driver_priv = NULL;
+ r = pm_runtime_get_sync(dev->dev);
+ if (r < 0)
+ return r;
+
/* new gpu have virtual address space support */
if (rdev->family >= CHIP_CAYMAN) {
struct radeon_fpriv *fpriv;
@@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
file_priv->driver_priv = fpriv;
}
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
return 0;
}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 18:18 ` [PATCH 2/2] drm/radeon: add runtime PM support (v2) Alex Deucher
@ 2013-09-20 20:25 ` Mike Lothian
2013-09-20 21:05 ` Alex Deucher
0 siblings, 1 reply; 10+ messages in thread
From: Mike Lothian @ 2013-09-20 20:25 UTC (permalink / raw)
To: Alex Deucher; +Cc: Alex Deucher, Dave Airlie, dri-devel
[-- Attachment #1.1: Type: text/plain, Size: 29543 bytes --]
Hi
Is there an easy way to check this is on?
I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
messages saying the card if now off at the moment I can old see messages
saying when the card gets powered up
Is it possible to have the on and off messages appearing?
Cheers
Mike
On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com> wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> This hooks radeon up to the runtime PM system to enable
> dynamic power management for secondary GPUs in switchable
> and powerxpress laptops.
>
> v2: agd5f: clean up, add module parameter
>
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
> ---
> drivers/gpu/drm/radeon/radeon.h | 8 +-
> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
> drivers/gpu/drm/radeon/radeon_drv.c | 122
> +++++++++++++++++++++++++--
> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
> 10 files changed, 299 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/gpu/drm/radeon/radeon.h
> b/drivers/gpu/drm/radeon/radeon.h
> index 986100a..ad54525 100644
> --- a/drivers/gpu/drm/radeon/radeon.h
> +++ b/drivers/gpu/drm/radeon/radeon.h
> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
> extern int radeon_fastfb;
> extern int radeon_dpm;
> extern int radeon_aspm;
> +extern int radeon_runtime_pm;
>
> /*
> * Copy from radeon_drv.h so we don't have to include both and have
> conflicting
> @@ -2212,6 +2213,9 @@ struct radeon_device {
> /* clock, powergating flags */
> u32 cg_flags;
> u32 pg_flags;
> +
> + struct dev_pm_domain vga_pm_domain;
> + bool have_disp_power_ref;
> };
>
> int radeon_device_init(struct radeon_device *rdev,
> @@ -2673,8 +2677,8 @@ extern void radeon_ttm_placement_from_domain(struct
> radeon_bo *rbo, u32 domain);
> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
> extern void radeon_vram_location(struct radeon_device *rdev, struct
> radeon_mc *mc, u64 base);
> extern void radeon_gtt_location(struct radeon_device *rdev, struct
> radeon_mc *mc);
> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> +extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> fbcon);
> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> fbcon);
> extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev,
> u64 size);
> extern void radeon_program_register_sequence(struct radeon_device *rdev,
> const u32 *registers,
> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> index d96070b..6153ec1 100644
> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> @@ -59,6 +59,10 @@ struct atpx_mux {
> u16 mux;
> } __packed;
>
> +bool radeon_is_px(void) {
> + return radeon_atpx_priv.atpx_detected;
> +}
> +
> /**
> * radeon_atpx_call - call an ATPX method
> *
> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
> b/drivers/gpu/drm/radeon/radeon_connectors.c
> index 79159b5..5855b5b 100644
> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> @@ -31,6 +31,8 @@
> #include "radeon.h"
> #include "atom.h"
>
> +#include <linux/pm_runtime.h>
> +
> extern void
> radeon_combios_connected_scratch_regs(struct drm_connector *connector,
> struct drm_encoder *encoder,
> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector *connector,
> bool force)
> struct radeon_connector *radeon_connector =
> to_radeon_connector(connector);
> struct drm_encoder *encoder =
> radeon_best_single_encoder(connector);
> enum drm_connector_status ret = connector_status_disconnected;
> + int r;
> +
> + r = pm_runtime_get_sync(connector->dev->dev);
> + if (r < 0)
> + return connector_status_disconnected;
>
> if (encoder) {
> struct radeon_encoder *radeon_encoder =
> to_radeon_encoder(encoder);
> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector *connector,
> bool force)
> /* check acpi lid status ??? */
>
> radeon_connector_update_scratch_regs(connector, ret);
> + pm_runtime_mark_last_busy(connector->dev->dev);
> + pm_runtime_put_autosuspend(connector->dev->dev);
> return ret;
> }
>
> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector *connector,
> bool force)
> struct drm_encoder_helper_funcs *encoder_funcs;
> bool dret = false;
> enum drm_connector_status ret = connector_status_disconnected;
> + int r;
> +
> + r = pm_runtime_get_sync(connector->dev->dev);
> + if (r < 0)
> + return connector_status_disconnected;
>
> encoder = radeon_best_single_encoder(connector);
> if (!encoder)
> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector *connector,
> bool force)
> * detected a monitor via load.
> */
> if (radeon_connector->detected_by_load)
> - return connector->status;
> - else
> - return ret;
> + ret = connector->status;
> + goto out;
> }
>
> if (radeon_connector->dac_load_detect && encoder) {
> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector *connector,
> bool force)
> }
>
> radeon_connector_update_scratch_regs(connector, ret);
> +
> +out:
> + pm_runtime_mark_last_busy(connector->dev->dev);
> + pm_runtime_put_autosuspend(connector->dev->dev);
> +
> return ret;
> }
>
> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector *connector,
> bool force)
> struct drm_encoder_helper_funcs *encoder_funcs;
> struct radeon_connector *radeon_connector =
> to_radeon_connector(connector);
> enum drm_connector_status ret = connector_status_disconnected;
> + int r;
>
> if (!radeon_connector->dac_load_detect)
> return ret;
>
> + r = pm_runtime_get_sync(connector->dev->dev);
> + if (r < 0)
> + return connector_status_disconnected;
> +
> encoder = radeon_best_single_encoder(connector);
> if (!encoder)
> ret = connector_status_disconnected;
> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector, bool
> force)
> if (ret == connector_status_connected)
> ret =
> radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret,
> false);
> radeon_connector_update_scratch_regs(connector, ret);
> + pm_runtime_mark_last_busy(connector->dev->dev);
> + pm_runtime_put_autosuspend(connector->dev->dev);
> return ret;
> }
>
> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector *connector,
> bool force)
> struct drm_encoder *encoder = NULL;
> struct drm_encoder_helper_funcs *encoder_funcs;
> struct drm_mode_object *obj;
> - int i;
> + int i, r;
> enum drm_connector_status ret = connector_status_disconnected;
> bool dret = false, broken_edid = false;
>
> - if (!force && radeon_check_hpd_status_unchanged(connector))
> - return connector->status;
> + r = pm_runtime_get_sync(connector->dev->dev);
> + if (r < 0)
> + return connector_status_disconnected;
> +
> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> + ret = connector->status;
> + goto exit;
> + }
>
> if (radeon_connector->ddc_bus)
> dret = radeon_ddc_probe(radeon_connector, false);
> @@ -1110,6 +1141,11 @@ out:
>
> /* updated in get modes as well since we need to know if it's
> analog or digital */
> radeon_connector_update_scratch_regs(connector, ret);
> +
> +exit:
> + pm_runtime_mark_last_busy(connector->dev->dev);
> + pm_runtime_put_autosuspend(connector->dev->dev);
> +
> return ret;
> }
>
> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector *connector,
> bool force)
> enum drm_connector_status ret = connector_status_disconnected;
> struct radeon_connector_atom_dig *radeon_dig_connector =
> radeon_connector->con_priv;
> struct drm_encoder *encoder =
> radeon_best_single_encoder(connector);
> + int r;
>
> - if (!force && radeon_check_hpd_status_unchanged(connector))
> - return connector->status;
> + r = pm_runtime_get_sync(connector->dev->dev);
> + if (r < 0)
> + return connector_status_disconnected;
> +
> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> + ret = connector->status;
> + goto out;
> + }
>
> if (radeon_connector->edid) {
> kfree(radeon_connector->edid);
> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector *connector,
> bool force)
> }
>
> radeon_connector_update_scratch_regs(connector, ret);
> +out:
> + pm_runtime_mark_last_busy(connector->dev->dev);
> + pm_runtime_put_autosuspend(connector->dev->dev);
> +
> return ret;
> }
>
> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
> b/drivers/gpu/drm/radeon/radeon_device.c
> index 37cfcee..b9b9dfd 100644
> --- a/drivers/gpu/drm/radeon/radeon_device.c
> +++ b/drivers/gpu/drm/radeon/radeon_device.c
> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
> "LAST",
> };
>
> +#if defined(CONFIG_VGA_SWITCHEROO)
> +bool radeon_is_px(void);
> +#else
> +static inline bool radeon_is_px(void) { return false; }
> +#endif
> +
> /**
> * radeon_program_register_sequence - program an array of registers.
> *
> @@ -1076,6 +1082,10 @@ static bool
> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
> vga_switcheroo_state state)
> {
> struct drm_device *dev = pci_get_drvdata(pdev);
> +
> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
> + return;
> +
> if (state == VGA_SWITCHEROO_ON) {
> unsigned d3_delay = dev->pdev->d3_delay;
>
> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
> pci_dev *pdev, enum vga_switchero
> if (d3_delay < 20 &&
> radeon_switcheroo_quirk_long_wakeup(pdev))
> dev->pdev->d3_delay = 20;
>
> - radeon_resume_kms(dev, 1);
> + radeon_resume_kms(dev, true, true);
>
> dev->pdev->d3_delay = d3_delay;
>
> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
> pci_dev *pdev, enum vga_switchero
> printk(KERN_INFO "radeon: switched off\n");
> drm_kms_helper_poll_disable(dev);
> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> - radeon_suspend_kms(dev, 1);
> + radeon_suspend_kms(dev, true, true);
> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
> }
> }
> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device *rdev,
> {
> int r, i;
> int dma_bits;
> + bool runtime = false;
>
> rdev->shutdown = false;
> rdev->dev = &pdev->dev;
> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device *rdev,
> /* this will fail for cards that aren't VGA class devices, just
> * ignore it */
> vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
> - vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops,
> false);
> +
> + if (radeon_runtime_pm == 1)
> + runtime = true;
> + if ((radeon_runtime_pm == -1) && radeon_is_px())
> + runtime = true;
> + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops,
> runtime);
> + if (runtime)
> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
> &rdev->vga_pm_domain);
>
> r = radeon_init(rdev);
> if (r)
> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device *rdev)
> * Returns 0 for success or an error on failure.
> * Called at driver suspend.
> */
> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
> {
> struct radeon_device *rdev;
> struct drm_crtc *crtc;
> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev, bool
> suspend)
> pci_disable_device(dev->pdev);
> pci_set_power_state(dev->pdev, PCI_D3hot);
> }
> - console_lock();
> - radeon_fbdev_set_suspend(rdev, 1);
> - console_unlock();
> +
> + if (fbcon) {
> + console_lock();
> + radeon_fbdev_set_suspend(rdev, 1);
> + console_unlock();
> + }
> return 0;
> }
>
> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool
> suspend)
> * Returns 0 for success or an error on failure.
> * Called at driver resume.
> */
> -int radeon_resume_kms(struct drm_device *dev, bool resume)
> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
> {
> struct drm_connector *connector;
> struct radeon_device *rdev = dev->dev_private;
> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev, bool
> resume)
> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
> return 0;
>
> - console_lock();
> + if (fbcon) {
> + console_lock();
> + }
> if (resume) {
> pci_set_power_state(dev->pdev, PCI_D0);
> pci_restore_state(dev->pdev);
> if (pci_enable_device(dev->pdev)) {
> - console_unlock();
> + if (fbcon)
> + console_unlock();
> return -1;
> }
> }
> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev, bool
> resume)
> radeon_pm_resume(rdev);
> radeon_restore_bios_scratch_regs(rdev);
>
> - radeon_fbdev_set_suspend(rdev, 0);
> - console_unlock();
> -
> + if (fbcon) {
> + radeon_fbdev_set_suspend(rdev, 0);
> + console_unlock();
> + }
> +
> /* init dig PHYs, disp eng pll */
> if (rdev->is_atom_bios) {
> radeon_atom_encoder_init(rdev);
> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
> b/drivers/gpu/drm/radeon/radeon_display.c
> index 0d1aa05..bc37e33 100644
> --- a/drivers/gpu/drm/radeon/radeon_display.c
> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> @@ -30,6 +30,7 @@
> #include "atom.h"
> #include <asm/div64.h>
>
> +#include <linux/pm_runtime.h>
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_edid.h>
>
> @@ -494,11 +495,55 @@ unlock_free:
> return r;
> }
>
> +static int
> +radeon_crtc_set_config(struct drm_mode_set *set)
> +{
> + struct drm_device *dev;
> + struct radeon_device *rdev;
> + struct drm_crtc *crtc;
> + bool active = false;
> + int ret;
> +
> + if (!set || !set->crtc)
> + return -EINVAL;
> +
> + dev = set->crtc->dev;
> +
> + ret = pm_runtime_get_sync(dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = drm_crtc_helper_set_config(set);
> +
> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> + if (crtc->enabled)
> + active = true;
> +
> + pm_runtime_mark_last_busy(dev->dev);
> +
> + rdev = dev->dev_private;
> + /* if we have active crtcs and we don't have a power ref,
> + take the current one */
> + if (active && !rdev->have_disp_power_ref) {
> + rdev->have_disp_power_ref = true;
> + return ret;
> + }
> + /* if we have no active crtcs, then drop the power ref
> + we got before */
> + if (!active && rdev->have_disp_power_ref) {
> + pm_runtime_put_autosuspend(dev->dev);
> + rdev->have_disp_power_ref = false;
> + }
> +
> + /* drop the power reference we got coming in here */
> + pm_runtime_put_autosuspend(dev->dev);
> + return ret;
> +}
> static const struct drm_crtc_funcs radeon_crtc_funcs = {
> .cursor_set = radeon_crtc_cursor_set,
> .cursor_move = radeon_crtc_cursor_move,
> .gamma_set = radeon_crtc_gamma_set,
> - .set_config = drm_crtc_helper_set_config,
> + .set_config = radeon_crtc_set_config,
> .destroy = radeon_crtc_destroy,
> .page_flip = radeon_crtc_page_flip,
> };
> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
> b/drivers/gpu/drm/radeon/radeon_drv.c
> index 788bfb0..427c64f 100644
> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> @@ -36,8 +36,9 @@
> #include <drm/drm_pciids.h>
> #include <linux/console.h>
> #include <linux/module.h>
> -
> -
> +#include <linux/pm_runtime.h>
> +#include <linux/vga_switcheroo.h>
> +#include "drm_crtc_helper.h"
> /*
> * KMS wrapper.
> * - 2.0.0 - initial interface
> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
> struct drm_file *file_priv);
> void radeon_driver_preclose_kms(struct drm_device *dev,
> struct drm_file *file_priv);
> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> -int radeon_resume_kms(struct drm_device *dev, bool resume);
> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor *minor);
> #if defined(CONFIG_VGA_SWITCHEROO)
> void radeon_register_atpx_handler(void);
> void radeon_unregister_atpx_handler(void);
> +bool radeon_is_px(void);
> #else
> static inline void radeon_register_atpx_handler(void) {}
> static inline void radeon_unregister_atpx_handler(void) {}
> +static inline bool radeon_is_px(void) { return false; }
> #endif
>
> int radeon_no_wb;
> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
> int radeon_fastfb = 0;
> int radeon_dpm = -1;
> int radeon_aspm = -1;
> +int radeon_runtime_pm = -1;
>
> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
> module_param_named(no_wb, radeon_no_wb, int, 0444);
> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
> auto)");
> module_param_named(aspm, radeon_aspm, int, 0444);
>
> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1
> = PX only default)");
> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
> +
> static struct pci_device_id pciidlist[] = {
> radeon_PCI_IDS
> };
> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
> return 0;
> }
>
> +
> static const struct file_operations radeon_driver_old_fops = {
> .owner = THIS_MODULE,
> .open = drm_open,
> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device *dev)
> {
> struct pci_dev *pdev = to_pci_dev(dev);
> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - return radeon_suspend_kms(drm_dev, 1);
> + return radeon_suspend_kms(drm_dev, true, true);
> }
>
> static int radeon_pmops_resume(struct device *dev)
> {
> struct pci_dev *pdev = to_pci_dev(dev);
> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - return radeon_resume_kms(drm_dev, 1);
> + return radeon_resume_kms(drm_dev, true, true);
> }
>
> static int radeon_pmops_freeze(struct device *dev)
> {
> struct pci_dev *pdev = to_pci_dev(dev);
> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - return radeon_suspend_kms(drm_dev, 0);
> + return radeon_suspend_kms(drm_dev, false, true);
> }
>
> static int radeon_pmops_thaw(struct device *dev)
> {
> struct pci_dev *pdev = to_pci_dev(dev);
> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - return radeon_resume_kms(drm_dev, 0);
> + return radeon_resume_kms(drm_dev, false, true);
> +}
> +
> +static int radeon_pmops_runtime_suspend(struct device *dev)
> +{
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> + int ret;
> +
> + if (radeon_runtime_pm == 0)
> + return -EINVAL;
> +
> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> + drm_kms_helper_poll_disable(drm_dev);
> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
> +
> + ret = radeon_suspend_kms(drm_dev, false, false);
> + pci_save_state(pdev);
> + pci_disable_device(pdev);
> + pci_set_power_state(pdev, PCI_D3cold);
> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
> +
> + return 0;
> +}
> +
> +static int radeon_pmops_runtime_resume(struct device *dev)
> +{
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> + int ret;
> +
> + if (radeon_runtime_pm == 0)
> + return -EINVAL;
> +
> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> +
> + pci_set_power_state(pdev, PCI_D0);
> + pci_restore_state(pdev);
> + ret = pci_enable_device(pdev);
> + if (ret)
> + return ret;
> + pci_set_master(pdev);
> +
> + ret = radeon_resume_kms(drm_dev, false, false);
> + drm_kms_helper_poll_enable(drm_dev);
> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
> + return 0;
> +}
> +
> +static int radeon_pmops_runtime_idle(struct device *dev)
> +{
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> + struct drm_crtc *crtc;
> +
> + if (radeon_runtime_pm == 0)
> + return -EBUSY;
> +
> + /* are we PX enabled? */
> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
> + return -EBUSY;
> + }
> +
> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
> + if (crtc->enabled) {
> + DRM_DEBUG_DRIVER("failing to power off - crtc
> active\n");
> + return -EBUSY;
> + }
> + }
> +
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_autosuspend(dev);
> + /* we don't want the main rpm_idle to call suspend - we want to
> autosuspend */
> + return 1;
> +}
> +
> +long radeon_drm_ioctl(struct file *filp,
> + unsigned int cmd, unsigned long arg)
> +{
> + struct drm_file *file_priv = filp->private_data;
> + struct drm_device *dev;
> + long ret;
> + dev = file_priv->minor->dev;
> + ret = pm_runtime_get_sync(dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = drm_ioctl(filp, cmd, arg);
> +
> + pm_runtime_mark_last_busy(dev->dev);
> + pm_runtime_put_autosuspend(dev->dev);
> + return ret;
> }
>
> static const struct dev_pm_ops radeon_pm_ops = {
> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops = {
> .thaw = radeon_pmops_thaw,
> .poweroff = radeon_pmops_freeze,
> .restore = radeon_pmops_resume,
> + .runtime_suspend = radeon_pmops_runtime_suspend,
> + .runtime_resume = radeon_pmops_runtime_resume,
> + .runtime_idle = radeon_pmops_runtime_idle,
> };
>
> static const struct file_operations radeon_driver_kms_fops = {
> .owner = THIS_MODULE,
> .open = drm_open,
> .release = drm_release,
> - .unlocked_ioctl = drm_ioctl,
> + .unlocked_ioctl = radeon_drm_ioctl,
> .mmap = radeon_mmap,
> .poll = drm_poll,
> .read = drm_read,
> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
> b/drivers/gpu/drm/radeon/radeon_drv.h
> index b369d42..543dcfa 100644
> --- a/drivers/gpu/drm/radeon/radeon_drv.h
> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
> @@ -113,6 +113,9 @@
> #define DRIVER_MINOR 33
> #define DRIVER_PATCHLEVEL 0
>
> +long radeon_drm_ioctl(struct file *filp,
> + unsigned int cmd, unsigned long arg);
> +
> /* The rest of the file is DEPRECATED! */
> #ifdef CONFIG_DRM_RADEON_UMS
>
> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
> b/drivers/gpu/drm/radeon/radeon_ioc32.c
> index c180df8..bdb0f93 100644
> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
> unsigned int cmd, unsigned long
> if (nr < DRM_COMMAND_BASE)
> return drm_compat_ioctl(filp, cmd, arg);
>
> - ret = drm_ioctl(filp, cmd, arg);
> + ret = radeon_drm_ioctl(filp, cmd, arg);
>
> return ret;
> }
> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> index cc9e848..ec6240b 100644
> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> @@ -32,6 +32,8 @@
> #include "radeon.h"
> #include "atom.h"
>
> +#include <linux/pm_runtime.h>
> +
> #define RADEON_WAIT_IDLE_TIMEOUT 200
>
> /**
> @@ -47,8 +49,12 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
> {
> struct drm_device *dev = (struct drm_device *) arg;
> struct radeon_device *rdev = dev->dev_private;
> + irqreturn_t ret;
>
> - return radeon_irq_process(rdev);
> + ret = radeon_irq_process(rdev);
> + if (ret == IRQ_HANDLED)
> + pm_runtime_mark_last_busy(dev->dev);
> + return ret;
> }
>
> /*
> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
> b/drivers/gpu/drm/radeon/radeon_kms.c
> index 61580dd..bffff51 100644
> --- a/drivers/gpu/drm/radeon/radeon_kms.c
> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
> @@ -32,7 +32,7 @@
>
> #include <linux/vga_switcheroo.h>
> #include <linux/slab.h>
> -
> +#include <linux/pm_runtime.h>
> /**
> * radeon_driver_unload_kms - Main unload function for KMS.
> *
> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
>
> if (rdev == NULL)
> return 0;
> +
> if (rdev->rmmio == NULL)
> goto done_free;
> +
> + pm_runtime_get_sync(dev->dev);
> +
> radeon_acpi_fini(rdev);
> +
> radeon_modeset_fini(rdev);
> radeon_device_fini(rdev);
>
> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev,
> unsigned long flags)
> "Error during ACPI methods call\n");
> }
>
> + if (radeon_runtime_pm != 0) {
> + pm_runtime_use_autosuspend(dev->dev);
> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
> + pm_runtime_set_active(dev->dev);
> + pm_runtime_allow(dev->dev);
> + pm_runtime_mark_last_busy(dev->dev);
> + pm_runtime_put_autosuspend(dev->dev);
> + }
> +
> out:
> if (r)
> radeon_driver_unload_kms(dev);
> +
> +
> return r;
> }
>
> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device
> *dev)
> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
> *file_priv)
> {
> struct radeon_device *rdev = dev->dev_private;
> + int r;
>
> file_priv->driver_priv = NULL;
>
> + r = pm_runtime_get_sync(dev->dev);
> + if (r < 0)
> + return r;
> +
> /* new gpu have virtual address space support */
> if (rdev->family >= CHIP_CAYMAN) {
> struct radeon_fpriv *fpriv;
> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev,
> struct drm_file *file_priv)
>
> file_priv->driver_priv = fpriv;
> }
> +
> + pm_runtime_mark_last_busy(dev->dev);
> + pm_runtime_put_autosuspend(dev->dev);
> return 0;
> }
>
> --
> 1.8.3.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
[-- Attachment #1.2: Type: text/html, Size: 33318 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 20:25 ` Mike Lothian
@ 2013-09-20 21:05 ` Alex Deucher
2013-09-20 22:10 ` Mike Lothian
0 siblings, 1 reply; 10+ messages in thread
From: Alex Deucher @ 2013-09-20 21:05 UTC (permalink / raw)
To: Mike Lothian; +Cc: Alex Deucher, Dave Airlie, Maling list - DRI developers
On Fri, Sep 20, 2013 at 4:25 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
> Hi
>
> Is there an easy way to check this is on?
It's on by default if your system is a powerxpress system (hybrid laptop).
>
> I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
> messages saying the card if now off at the moment I can old see messages
> saying when the card gets powered up
>
The option is radeon.runpm for this. Note that only powerxpress
systems are supported. There is no support for powering down
arbitrary cards yet.
> Is it possible to have the on and off messages appearing?
On a supported system, you will see suspend and resume messages when
when the card is powered down/up.
Alex
>
> Cheers
>
> Mike
>
>
> On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com> wrote:
>>
>> From: Dave Airlie <airlied@redhat.com>
>>
>> This hooks radeon up to the runtime PM system to enable
>> dynamic power management for secondary GPUs in switchable
>> and powerxpress laptops.
>>
>> v2: agd5f: clean up, add module parameter
>>
>> Signed-off-by: Dave Airlie <airlied@redhat.com>
>> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
>> ---
>> drivers/gpu/drm/radeon/radeon.h | 8 +-
>> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
>> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
>> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
>> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
>> drivers/gpu/drm/radeon/radeon_drv.c | 122
>> +++++++++++++++++++++++++--
>> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
>> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
>> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
>> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
>> 10 files changed, 299 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/radeon/radeon.h
>> b/drivers/gpu/drm/radeon/radeon.h
>> index 986100a..ad54525 100644
>> --- a/drivers/gpu/drm/radeon/radeon.h
>> +++ b/drivers/gpu/drm/radeon/radeon.h
>> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
>> extern int radeon_fastfb;
>> extern int radeon_dpm;
>> extern int radeon_aspm;
>> +extern int radeon_runtime_pm;
>>
>> /*
>> * Copy from radeon_drv.h so we don't have to include both and have
>> conflicting
>> @@ -2212,6 +2213,9 @@ struct radeon_device {
>> /* clock, powergating flags */
>> u32 cg_flags;
>> u32 pg_flags;
>> +
>> + struct dev_pm_domain vga_pm_domain;
>> + bool have_disp_power_ref;
>> };
>>
>> int radeon_device_init(struct radeon_device *rdev,
>> @@ -2673,8 +2677,8 @@ extern void radeon_ttm_placement_from_domain(struct
>> radeon_bo *rbo, u32 domain);
>> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
>> extern void radeon_vram_location(struct radeon_device *rdev, struct
>> radeon_mc *mc, u64 base);
>> extern void radeon_gtt_location(struct radeon_device *rdev, struct
>> radeon_mc *mc);
>> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
>> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
>> +extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool
>> fbcon);
>> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
>> fbcon);
>> extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev,
>> u64 size);
>> extern void radeon_program_register_sequence(struct radeon_device *rdev,
>> const u32 *registers,
>> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> index d96070b..6153ec1 100644
>> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> @@ -59,6 +59,10 @@ struct atpx_mux {
>> u16 mux;
>> } __packed;
>>
>> +bool radeon_is_px(void) {
>> + return radeon_atpx_priv.atpx_detected;
>> +}
>> +
>> /**
>> * radeon_atpx_call - call an ATPX method
>> *
>> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
>> b/drivers/gpu/drm/radeon/radeon_connectors.c
>> index 79159b5..5855b5b 100644
>> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
>> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
>> @@ -31,6 +31,8 @@
>> #include "radeon.h"
>> #include "atom.h"
>>
>> +#include <linux/pm_runtime.h>
>> +
>> extern void
>> radeon_combios_connected_scratch_regs(struct drm_connector *connector,
>> struct drm_encoder *encoder,
>> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector *connector,
>> bool force)
>> struct radeon_connector *radeon_connector =
>> to_radeon_connector(connector);
>> struct drm_encoder *encoder =
>> radeon_best_single_encoder(connector);
>> enum drm_connector_status ret = connector_status_disconnected;
>> + int r;
>> +
>> + r = pm_runtime_get_sync(connector->dev->dev);
>> + if (r < 0)
>> + return connector_status_disconnected;
>>
>> if (encoder) {
>> struct radeon_encoder *radeon_encoder =
>> to_radeon_encoder(encoder);
>> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector *connector,
>> bool force)
>> /* check acpi lid status ??? */
>>
>> radeon_connector_update_scratch_regs(connector, ret);
>> + pm_runtime_mark_last_busy(connector->dev->dev);
>> + pm_runtime_put_autosuspend(connector->dev->dev);
>> return ret;
>> }
>>
>> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector *connector,
>> bool force)
>> struct drm_encoder_helper_funcs *encoder_funcs;
>> bool dret = false;
>> enum drm_connector_status ret = connector_status_disconnected;
>> + int r;
>> +
>> + r = pm_runtime_get_sync(connector->dev->dev);
>> + if (r < 0)
>> + return connector_status_disconnected;
>>
>> encoder = radeon_best_single_encoder(connector);
>> if (!encoder)
>> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector *connector,
>> bool force)
>> * detected a monitor via load.
>> */
>> if (radeon_connector->detected_by_load)
>> - return connector->status;
>> - else
>> - return ret;
>> + ret = connector->status;
>> + goto out;
>> }
>>
>> if (radeon_connector->dac_load_detect && encoder) {
>> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector *connector,
>> bool force)
>> }
>>
>> radeon_connector_update_scratch_regs(connector, ret);
>> +
>> +out:
>> + pm_runtime_mark_last_busy(connector->dev->dev);
>> + pm_runtime_put_autosuspend(connector->dev->dev);
>> +
>> return ret;
>> }
>>
>> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector *connector,
>> bool force)
>> struct drm_encoder_helper_funcs *encoder_funcs;
>> struct radeon_connector *radeon_connector =
>> to_radeon_connector(connector);
>> enum drm_connector_status ret = connector_status_disconnected;
>> + int r;
>>
>> if (!radeon_connector->dac_load_detect)
>> return ret;
>>
>> + r = pm_runtime_get_sync(connector->dev->dev);
>> + if (r < 0)
>> + return connector_status_disconnected;
>> +
>> encoder = radeon_best_single_encoder(connector);
>> if (!encoder)
>> ret = connector_status_disconnected;
>> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector, bool
>> force)
>> if (ret == connector_status_connected)
>> ret =
>> radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret,
>> false);
>> radeon_connector_update_scratch_regs(connector, ret);
>> + pm_runtime_mark_last_busy(connector->dev->dev);
>> + pm_runtime_put_autosuspend(connector->dev->dev);
>> return ret;
>> }
>>
>> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector *connector,
>> bool force)
>> struct drm_encoder *encoder = NULL;
>> struct drm_encoder_helper_funcs *encoder_funcs;
>> struct drm_mode_object *obj;
>> - int i;
>> + int i, r;
>> enum drm_connector_status ret = connector_status_disconnected;
>> bool dret = false, broken_edid = false;
>>
>> - if (!force && radeon_check_hpd_status_unchanged(connector))
>> - return connector->status;
>> + r = pm_runtime_get_sync(connector->dev->dev);
>> + if (r < 0)
>> + return connector_status_disconnected;
>> +
>> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
>> + ret = connector->status;
>> + goto exit;
>> + }
>>
>> if (radeon_connector->ddc_bus)
>> dret = radeon_ddc_probe(radeon_connector, false);
>> @@ -1110,6 +1141,11 @@ out:
>>
>> /* updated in get modes as well since we need to know if it's
>> analog or digital */
>> radeon_connector_update_scratch_regs(connector, ret);
>> +
>> +exit:
>> + pm_runtime_mark_last_busy(connector->dev->dev);
>> + pm_runtime_put_autosuspend(connector->dev->dev);
>> +
>> return ret;
>> }
>>
>> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector *connector,
>> bool force)
>> enum drm_connector_status ret = connector_status_disconnected;
>> struct radeon_connector_atom_dig *radeon_dig_connector =
>> radeon_connector->con_priv;
>> struct drm_encoder *encoder =
>> radeon_best_single_encoder(connector);
>> + int r;
>>
>> - if (!force && radeon_check_hpd_status_unchanged(connector))
>> - return connector->status;
>> + r = pm_runtime_get_sync(connector->dev->dev);
>> + if (r < 0)
>> + return connector_status_disconnected;
>> +
>> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
>> + ret = connector->status;
>> + goto out;
>> + }
>>
>> if (radeon_connector->edid) {
>> kfree(radeon_connector->edid);
>> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector *connector,
>> bool force)
>> }
>>
>> radeon_connector_update_scratch_regs(connector, ret);
>> +out:
>> + pm_runtime_mark_last_busy(connector->dev->dev);
>> + pm_runtime_put_autosuspend(connector->dev->dev);
>> +
>> return ret;
>> }
>>
>> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
>> b/drivers/gpu/drm/radeon/radeon_device.c
>> index 37cfcee..b9b9dfd 100644
>> --- a/drivers/gpu/drm/radeon/radeon_device.c
>> +++ b/drivers/gpu/drm/radeon/radeon_device.c
>> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
>> "LAST",
>> };
>>
>> +#if defined(CONFIG_VGA_SWITCHEROO)
>> +bool radeon_is_px(void);
>> +#else
>> +static inline bool radeon_is_px(void) { return false; }
>> +#endif
>> +
>> /**
>> * radeon_program_register_sequence - program an array of registers.
>> *
>> @@ -1076,6 +1082,10 @@ static bool
>> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
>> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
>> vga_switcheroo_state state)
>> {
>> struct drm_device *dev = pci_get_drvdata(pdev);
>> +
>> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
>> + return;
>> +
>> if (state == VGA_SWITCHEROO_ON) {
>> unsigned d3_delay = dev->pdev->d3_delay;
>>
>> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
>> pci_dev *pdev, enum vga_switchero
>> if (d3_delay < 20 &&
>> radeon_switcheroo_quirk_long_wakeup(pdev))
>> dev->pdev->d3_delay = 20;
>>
>> - radeon_resume_kms(dev, 1);
>> + radeon_resume_kms(dev, true, true);
>>
>> dev->pdev->d3_delay = d3_delay;
>>
>> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
>> pci_dev *pdev, enum vga_switchero
>> printk(KERN_INFO "radeon: switched off\n");
>> drm_kms_helper_poll_disable(dev);
>> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> - radeon_suspend_kms(dev, 1);
>> + radeon_suspend_kms(dev, true, true);
>> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
>> }
>> }
>> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device *rdev,
>> {
>> int r, i;
>> int dma_bits;
>> + bool runtime = false;
>>
>> rdev->shutdown = false;
>> rdev->dev = &pdev->dev;
>> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device *rdev,
>> /* this will fail for cards that aren't VGA class devices, just
>> * ignore it */
>> vga_client_register(rdev->pdev, rdev, NULL,
>> radeon_vga_set_decode);
>> - vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops,
>> false);
>> +
>> + if (radeon_runtime_pm == 1)
>> + runtime = true;
>> + if ((radeon_runtime_pm == -1) && radeon_is_px())
>> + runtime = true;
>> + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops,
>> runtime);
>> + if (runtime)
>> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
>> &rdev->vga_pm_domain);
>>
>> r = radeon_init(rdev);
>> if (r)
>> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device *rdev)
>> * Returns 0 for success or an error on failure.
>> * Called at driver suspend.
>> */
>> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
>> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
>> {
>> struct radeon_device *rdev;
>> struct drm_crtc *crtc;
>> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev, bool
>> suspend)
>> pci_disable_device(dev->pdev);
>> pci_set_power_state(dev->pdev, PCI_D3hot);
>> }
>> - console_lock();
>> - radeon_fbdev_set_suspend(rdev, 1);
>> - console_unlock();
>> +
>> + if (fbcon) {
>> + console_lock();
>> + radeon_fbdev_set_suspend(rdev, 1);
>> + console_unlock();
>> + }
>> return 0;
>> }
>>
>> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool
>> suspend)
>> * Returns 0 for success or an error on failure.
>> * Called at driver resume.
>> */
>> -int radeon_resume_kms(struct drm_device *dev, bool resume)
>> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
>> {
>> struct drm_connector *connector;
>> struct radeon_device *rdev = dev->dev_private;
>> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev, bool
>> resume)
>> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
>> return 0;
>>
>> - console_lock();
>> + if (fbcon) {
>> + console_lock();
>> + }
>> if (resume) {
>> pci_set_power_state(dev->pdev, PCI_D0);
>> pci_restore_state(dev->pdev);
>> if (pci_enable_device(dev->pdev)) {
>> - console_unlock();
>> + if (fbcon)
>> + console_unlock();
>> return -1;
>> }
>> }
>> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev, bool
>> resume)
>> radeon_pm_resume(rdev);
>> radeon_restore_bios_scratch_regs(rdev);
>>
>> - radeon_fbdev_set_suspend(rdev, 0);
>> - console_unlock();
>> -
>> + if (fbcon) {
>> + radeon_fbdev_set_suspend(rdev, 0);
>> + console_unlock();
>> + }
>> +
>> /* init dig PHYs, disp eng pll */
>> if (rdev->is_atom_bios) {
>> radeon_atom_encoder_init(rdev);
>> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
>> b/drivers/gpu/drm/radeon/radeon_display.c
>> index 0d1aa05..bc37e33 100644
>> --- a/drivers/gpu/drm/radeon/radeon_display.c
>> +++ b/drivers/gpu/drm/radeon/radeon_display.c
>> @@ -30,6 +30,7 @@
>> #include "atom.h"
>> #include <asm/div64.h>
>>
>> +#include <linux/pm_runtime.h>
>> #include <drm/drm_crtc_helper.h>
>> #include <drm/drm_edid.h>
>>
>> @@ -494,11 +495,55 @@ unlock_free:
>> return r;
>> }
>>
>> +static int
>> +radeon_crtc_set_config(struct drm_mode_set *set)
>> +{
>> + struct drm_device *dev;
>> + struct radeon_device *rdev;
>> + struct drm_crtc *crtc;
>> + bool active = false;
>> + int ret;
>> +
>> + if (!set || !set->crtc)
>> + return -EINVAL;
>> +
>> + dev = set->crtc->dev;
>> +
>> + ret = pm_runtime_get_sync(dev->dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = drm_crtc_helper_set_config(set);
>> +
>> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>> + if (crtc->enabled)
>> + active = true;
>> +
>> + pm_runtime_mark_last_busy(dev->dev);
>> +
>> + rdev = dev->dev_private;
>> + /* if we have active crtcs and we don't have a power ref,
>> + take the current one */
>> + if (active && !rdev->have_disp_power_ref) {
>> + rdev->have_disp_power_ref = true;
>> + return ret;
>> + }
>> + /* if we have no active crtcs, then drop the power ref
>> + we got before */
>> + if (!active && rdev->have_disp_power_ref) {
>> + pm_runtime_put_autosuspend(dev->dev);
>> + rdev->have_disp_power_ref = false;
>> + }
>> +
>> + /* drop the power reference we got coming in here */
>> + pm_runtime_put_autosuspend(dev->dev);
>> + return ret;
>> +}
>> static const struct drm_crtc_funcs radeon_crtc_funcs = {
>> .cursor_set = radeon_crtc_cursor_set,
>> .cursor_move = radeon_crtc_cursor_move,
>> .gamma_set = radeon_crtc_gamma_set,
>> - .set_config = drm_crtc_helper_set_config,
>> + .set_config = radeon_crtc_set_config,
>> .destroy = radeon_crtc_destroy,
>> .page_flip = radeon_crtc_page_flip,
>> };
>> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
>> b/drivers/gpu/drm/radeon/radeon_drv.c
>> index 788bfb0..427c64f 100644
>> --- a/drivers/gpu/drm/radeon/radeon_drv.c
>> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
>> @@ -36,8 +36,9 @@
>> #include <drm/drm_pciids.h>
>> #include <linux/console.h>
>> #include <linux/module.h>
>> -
>> -
>> +#include <linux/pm_runtime.h>
>> +#include <linux/vga_switcheroo.h>
>> +#include "drm_crtc_helper.h"
>> /*
>> * KMS wrapper.
>> * - 2.0.0 - initial interface
>> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
>> struct drm_file *file_priv);
>> void radeon_driver_preclose_kms(struct drm_device *dev,
>> struct drm_file *file_priv);
>> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
>> -int radeon_resume_kms(struct drm_device *dev, bool resume);
>> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
>> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
>> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
>> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
>> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
>> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor *minor);
>> #if defined(CONFIG_VGA_SWITCHEROO)
>> void radeon_register_atpx_handler(void);
>> void radeon_unregister_atpx_handler(void);
>> +bool radeon_is_px(void);
>> #else
>> static inline void radeon_register_atpx_handler(void) {}
>> static inline void radeon_unregister_atpx_handler(void) {}
>> +static inline bool radeon_is_px(void) { return false; }
>> #endif
>>
>> int radeon_no_wb;
>> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
>> int radeon_fastfb = 0;
>> int radeon_dpm = -1;
>> int radeon_aspm = -1;
>> +int radeon_runtime_pm = -1;
>>
>> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
>> module_param_named(no_wb, radeon_no_wb, int, 0444);
>> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
>> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
>> auto)");
>> module_param_named(aspm, radeon_aspm, int, 0444);
>>
>> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1
>> = PX only default)");
>> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
>> +
>> static struct pci_device_id pciidlist[] = {
>> radeon_PCI_IDS
>> };
>> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
>> return 0;
>> }
>>
>> +
>> static const struct file_operations radeon_driver_old_fops = {
>> .owner = THIS_MODULE,
>> .open = drm_open,
>> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device *dev)
>> {
>> struct pci_dev *pdev = to_pci_dev(dev);
>> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> - return radeon_suspend_kms(drm_dev, 1);
>> + return radeon_suspend_kms(drm_dev, true, true);
>> }
>>
>> static int radeon_pmops_resume(struct device *dev)
>> {
>> struct pci_dev *pdev = to_pci_dev(dev);
>> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> - return radeon_resume_kms(drm_dev, 1);
>> + return radeon_resume_kms(drm_dev, true, true);
>> }
>>
>> static int radeon_pmops_freeze(struct device *dev)
>> {
>> struct pci_dev *pdev = to_pci_dev(dev);
>> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> - return radeon_suspend_kms(drm_dev, 0);
>> + return radeon_suspend_kms(drm_dev, false, true);
>> }
>>
>> static int radeon_pmops_thaw(struct device *dev)
>> {
>> struct pci_dev *pdev = to_pci_dev(dev);
>> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> - return radeon_resume_kms(drm_dev, 0);
>> + return radeon_resume_kms(drm_dev, false, true);
>> +}
>> +
>> +static int radeon_pmops_runtime_suspend(struct device *dev)
>> +{
>> + struct pci_dev *pdev = to_pci_dev(dev);
>> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> + int ret;
>> +
>> + if (radeon_runtime_pm == 0)
>> + return -EINVAL;
>> +
>> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> + drm_kms_helper_poll_disable(drm_dev);
>> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
>> +
>> + ret = radeon_suspend_kms(drm_dev, false, false);
>> + pci_save_state(pdev);
>> + pci_disable_device(pdev);
>> + pci_set_power_state(pdev, PCI_D3cold);
>> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
>> +
>> + return 0;
>> +}
>> +
>> +static int radeon_pmops_runtime_resume(struct device *dev)
>> +{
>> + struct pci_dev *pdev = to_pci_dev(dev);
>> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> + int ret;
>> +
>> + if (radeon_runtime_pm == 0)
>> + return -EINVAL;
>> +
>> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> +
>> + pci_set_power_state(pdev, PCI_D0);
>> + pci_restore_state(pdev);
>> + ret = pci_enable_device(pdev);
>> + if (ret)
>> + return ret;
>> + pci_set_master(pdev);
>> +
>> + ret = radeon_resume_kms(drm_dev, false, false);
>> + drm_kms_helper_poll_enable(drm_dev);
>> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
>> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
>> + return 0;
>> +}
>> +
>> +static int radeon_pmops_runtime_idle(struct device *dev)
>> +{
>> + struct pci_dev *pdev = to_pci_dev(dev);
>> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> + struct drm_crtc *crtc;
>> +
>> + if (radeon_runtime_pm == 0)
>> + return -EBUSY;
>> +
>> + /* are we PX enabled? */
>> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
>> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
>> + return -EBUSY;
>> + }
>> +
>> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
>> + if (crtc->enabled) {
>> + DRM_DEBUG_DRIVER("failing to power off - crtc
>> active\n");
>> + return -EBUSY;
>> + }
>> + }
>> +
>> + pm_runtime_mark_last_busy(dev);
>> + pm_runtime_autosuspend(dev);
>> + /* we don't want the main rpm_idle to call suspend - we want to
>> autosuspend */
>> + return 1;
>> +}
>> +
>> +long radeon_drm_ioctl(struct file *filp,
>> + unsigned int cmd, unsigned long arg)
>> +{
>> + struct drm_file *file_priv = filp->private_data;
>> + struct drm_device *dev;
>> + long ret;
>> + dev = file_priv->minor->dev;
>> + ret = pm_runtime_get_sync(dev->dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = drm_ioctl(filp, cmd, arg);
>> +
>> + pm_runtime_mark_last_busy(dev->dev);
>> + pm_runtime_put_autosuspend(dev->dev);
>> + return ret;
>> }
>>
>> static const struct dev_pm_ops radeon_pm_ops = {
>> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops = {
>> .thaw = radeon_pmops_thaw,
>> .poweroff = radeon_pmops_freeze,
>> .restore = radeon_pmops_resume,
>> + .runtime_suspend = radeon_pmops_runtime_suspend,
>> + .runtime_resume = radeon_pmops_runtime_resume,
>> + .runtime_idle = radeon_pmops_runtime_idle,
>> };
>>
>> static const struct file_operations radeon_driver_kms_fops = {
>> .owner = THIS_MODULE,
>> .open = drm_open,
>> .release = drm_release,
>> - .unlocked_ioctl = drm_ioctl,
>> + .unlocked_ioctl = radeon_drm_ioctl,
>> .mmap = radeon_mmap,
>> .poll = drm_poll,
>> .read = drm_read,
>> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
>> b/drivers/gpu/drm/radeon/radeon_drv.h
>> index b369d42..543dcfa 100644
>> --- a/drivers/gpu/drm/radeon/radeon_drv.h
>> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
>> @@ -113,6 +113,9 @@
>> #define DRIVER_MINOR 33
>> #define DRIVER_PATCHLEVEL 0
>>
>> +long radeon_drm_ioctl(struct file *filp,
>> + unsigned int cmd, unsigned long arg);
>> +
>> /* The rest of the file is DEPRECATED! */
>> #ifdef CONFIG_DRM_RADEON_UMS
>>
>> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
>> b/drivers/gpu/drm/radeon/radeon_ioc32.c
>> index c180df8..bdb0f93 100644
>> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
>> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
>> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
>> unsigned int cmd, unsigned long
>> if (nr < DRM_COMMAND_BASE)
>> return drm_compat_ioctl(filp, cmd, arg);
>>
>> - ret = drm_ioctl(filp, cmd, arg);
>> + ret = radeon_drm_ioctl(filp, cmd, arg);
>>
>> return ret;
>> }
>> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> index cc9e848..ec6240b 100644
>> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> @@ -32,6 +32,8 @@
>> #include "radeon.h"
>> #include "atom.h"
>>
>> +#include <linux/pm_runtime.h>
>> +
>> #define RADEON_WAIT_IDLE_TIMEOUT 200
>>
>> /**
>> @@ -47,8 +49,12 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
>> {
>> struct drm_device *dev = (struct drm_device *) arg;
>> struct radeon_device *rdev = dev->dev_private;
>> + irqreturn_t ret;
>>
>> - return radeon_irq_process(rdev);
>> + ret = radeon_irq_process(rdev);
>> + if (ret == IRQ_HANDLED)
>> + pm_runtime_mark_last_busy(dev->dev);
>> + return ret;
>> }
>>
>> /*
>> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
>> b/drivers/gpu/drm/radeon/radeon_kms.c
>> index 61580dd..bffff51 100644
>> --- a/drivers/gpu/drm/radeon/radeon_kms.c
>> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
>> @@ -32,7 +32,7 @@
>>
>> #include <linux/vga_switcheroo.h>
>> #include <linux/slab.h>
>> -
>> +#include <linux/pm_runtime.h>
>> /**
>> * radeon_driver_unload_kms - Main unload function for KMS.
>> *
>> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
>>
>> if (rdev == NULL)
>> return 0;
>> +
>> if (rdev->rmmio == NULL)
>> goto done_free;
>> +
>> + pm_runtime_get_sync(dev->dev);
>> +
>> radeon_acpi_fini(rdev);
>> +
>> radeon_modeset_fini(rdev);
>> radeon_device_fini(rdev);
>>
>> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev,
>> unsigned long flags)
>> "Error during ACPI methods call\n");
>> }
>>
>> + if (radeon_runtime_pm != 0) {
>> + pm_runtime_use_autosuspend(dev->dev);
>> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
>> + pm_runtime_set_active(dev->dev);
>> + pm_runtime_allow(dev->dev);
>> + pm_runtime_mark_last_busy(dev->dev);
>> + pm_runtime_put_autosuspend(dev->dev);
>> + }
>> +
>> out:
>> if (r)
>> radeon_driver_unload_kms(dev);
>> +
>> +
>> return r;
>> }
>>
>> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device
>> *dev)
>> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
>> *file_priv)
>> {
>> struct radeon_device *rdev = dev->dev_private;
>> + int r;
>>
>> file_priv->driver_priv = NULL;
>>
>> + r = pm_runtime_get_sync(dev->dev);
>> + if (r < 0)
>> + return r;
>> +
>> /* new gpu have virtual address space support */
>> if (rdev->family >= CHIP_CAYMAN) {
>> struct radeon_fpriv *fpriv;
>> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev,
>> struct drm_file *file_priv)
>>
>> file_priv->driver_priv = fpriv;
>> }
>> +
>> + pm_runtime_mark_last_busy(dev->dev);
>> + pm_runtime_put_autosuspend(dev->dev);
>> return 0;
>> }
>>
>> --
>> 1.8.3.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 21:05 ` Alex Deucher
@ 2013-09-20 22:10 ` Mike Lothian
2013-09-20 22:12 ` Alex Deucher
0 siblings, 1 reply; 10+ messages in thread
From: Mike Lothian @ 2013-09-20 22:10 UTC (permalink / raw)
To: alexdeucher@gmail com
Cc: Alex Deucher, Dave Airlie, Maling list - DRI developers
[-- Attachment #1.1: Type: text/plain, Size: 33052 bytes --]
Sorry that was a typo on my part. I'm using radeon.runpm=1
I can see audio is switched off
[drm] Disabling audio 0 support
When I use DRI_PRIME=1 I see the DRM initialisation for my card each time I
fire up an application or run glxinfo
There isn't any explicit messages that the card is on of off however
How can I tell if I have a powerxpress card?
Thanks again
Mike
On 20 Sep 2013 22:05, "Alex Deucher" <alexdeucher@gmail.com> wrote:
> On Fri, Sep 20, 2013 at 4:25 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
> > Hi
> >
> > Is there an easy way to check this is on?
>
> It's on by default if your system is a powerxpress system (hybrid laptop).
>
> >
> > I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
> > messages saying the card if now off at the moment I can old see messages
> > saying when the card gets powered up
> >
>
> The option is radeon.runpm for this. Note that only powerxpress
> systems are supported. There is no support for powering down
> arbitrary cards yet.
>
> > Is it possible to have the on and off messages appearing?
>
> On a supported system, you will see suspend and resume messages when
> when the card is powered down/up.
>
> Alex
>
> >
> > Cheers
> >
> > Mike
> >
> >
> > On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com> wrote:
> >>
> >> From: Dave Airlie <airlied@redhat.com>
> >>
> >> This hooks radeon up to the runtime PM system to enable
> >> dynamic power management for secondary GPUs in switchable
> >> and powerxpress laptops.
> >>
> >> v2: agd5f: clean up, add module parameter
> >>
> >> Signed-off-by: Dave Airlie <airlied@redhat.com>
> >> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
> >> ---
> >> drivers/gpu/drm/radeon/radeon.h | 8 +-
> >> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
> >> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
> >> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
> >> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
> >> drivers/gpu/drm/radeon/radeon_drv.c | 122
> >> +++++++++++++++++++++++++--
> >> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
> >> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
> >> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
> >> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
> >> 10 files changed, 299 insertions(+), 36 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/radeon/radeon.h
> >> b/drivers/gpu/drm/radeon/radeon.h
> >> index 986100a..ad54525 100644
> >> --- a/drivers/gpu/drm/radeon/radeon.h
> >> +++ b/drivers/gpu/drm/radeon/radeon.h
> >> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
> >> extern int radeon_fastfb;
> >> extern int radeon_dpm;
> >> extern int radeon_aspm;
> >> +extern int radeon_runtime_pm;
> >>
> >> /*
> >> * Copy from radeon_drv.h so we don't have to include both and have
> >> conflicting
> >> @@ -2212,6 +2213,9 @@ struct radeon_device {
> >> /* clock, powergating flags */
> >> u32 cg_flags;
> >> u32 pg_flags;
> >> +
> >> + struct dev_pm_domain vga_pm_domain;
> >> + bool have_disp_power_ref;
> >> };
> >>
> >> int radeon_device_init(struct radeon_device *rdev,
> >> @@ -2673,8 +2677,8 @@ extern void
> radeon_ttm_placement_from_domain(struct
> >> radeon_bo *rbo, u32 domain);
> >> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
> >> extern void radeon_vram_location(struct radeon_device *rdev, struct
> >> radeon_mc *mc, u64 base);
> >> extern void radeon_gtt_location(struct radeon_device *rdev, struct
> >> radeon_mc *mc);
> >> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> +extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> >> fbcon);
> >> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend,
> bool
> >> fbcon);
> >> extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev,
> >> u64 size);
> >> extern void radeon_program_register_sequence(struct radeon_device
> *rdev,
> >> const u32 *registers,
> >> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> index d96070b..6153ec1 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> @@ -59,6 +59,10 @@ struct atpx_mux {
> >> u16 mux;
> >> } __packed;
> >>
> >> +bool radeon_is_px(void) {
> >> + return radeon_atpx_priv.atpx_detected;
> >> +}
> >> +
> >> /**
> >> * radeon_atpx_call - call an ATPX method
> >> *
> >> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> index 79159b5..5855b5b 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> @@ -31,6 +31,8 @@
> >> #include "radeon.h"
> >> #include "atom.h"
> >>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> extern void
> >> radeon_combios_connected_scratch_regs(struct drm_connector *connector,
> >> struct drm_encoder *encoder,
> >> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector *connector,
> >> bool force)
> >> struct radeon_connector *radeon_connector =
> >> to_radeon_connector(connector);
> >> struct drm_encoder *encoder =
> >> radeon_best_single_encoder(connector);
> >> enum drm_connector_status ret = connector_status_disconnected;
> >> + int r;
> >> +
> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> + if (r < 0)
> >> + return connector_status_disconnected;
> >>
> >> if (encoder) {
> >> struct radeon_encoder *radeon_encoder =
> >> to_radeon_encoder(encoder);
> >> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector *connector,
> >> bool force)
> >> /* check acpi lid status ??? */
> >>
> >> radeon_connector_update_scratch_regs(connector, ret);
> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> return ret;
> >> }
> >>
> >> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector *connector,
> >> bool force)
> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> bool dret = false;
> >> enum drm_connector_status ret = connector_status_disconnected;
> >> + int r;
> >> +
> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> + if (r < 0)
> >> + return connector_status_disconnected;
> >>
> >> encoder = radeon_best_single_encoder(connector);
> >> if (!encoder)
> >> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector *connector,
> >> bool force)
> >> * detected a monitor via load.
> >> */
> >> if (radeon_connector->detected_by_load)
> >> - return connector->status;
> >> - else
> >> - return ret;
> >> + ret = connector->status;
> >> + goto out;
> >> }
> >>
> >> if (radeon_connector->dac_load_detect && encoder) {
> >> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector *connector,
> >> bool force)
> >> }
> >>
> >> radeon_connector_update_scratch_regs(connector, ret);
> >> +
> >> +out:
> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> +
> >> return ret;
> >> }
> >>
> >> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector *connector,
> >> bool force)
> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> struct radeon_connector *radeon_connector =
> >> to_radeon_connector(connector);
> >> enum drm_connector_status ret = connector_status_disconnected;
> >> + int r;
> >>
> >> if (!radeon_connector->dac_load_detect)
> >> return ret;
> >>
> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> + if (r < 0)
> >> + return connector_status_disconnected;
> >> +
> >> encoder = radeon_best_single_encoder(connector);
> >> if (!encoder)
> >> ret = connector_status_disconnected;
> >> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector,
> bool
> >> force)
> >> if (ret == connector_status_connected)
> >> ret =
> >> radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret,
> >> false);
> >> radeon_connector_update_scratch_regs(connector, ret);
> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> return ret;
> >> }
> >>
> >> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector *connector,
> >> bool force)
> >> struct drm_encoder *encoder = NULL;
> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> struct drm_mode_object *obj;
> >> - int i;
> >> + int i, r;
> >> enum drm_connector_status ret = connector_status_disconnected;
> >> bool dret = false, broken_edid = false;
> >>
> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> - return connector->status;
> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> + if (r < 0)
> >> + return connector_status_disconnected;
> >> +
> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> + ret = connector->status;
> >> + goto exit;
> >> + }
> >>
> >> if (radeon_connector->ddc_bus)
> >> dret = radeon_ddc_probe(radeon_connector, false);
> >> @@ -1110,6 +1141,11 @@ out:
> >>
> >> /* updated in get modes as well since we need to know if it's
> >> analog or digital */
> >> radeon_connector_update_scratch_regs(connector, ret);
> >> +
> >> +exit:
> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> +
> >> return ret;
> >> }
> >>
> >> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector *connector,
> >> bool force)
> >> enum drm_connector_status ret = connector_status_disconnected;
> >> struct radeon_connector_atom_dig *radeon_dig_connector =
> >> radeon_connector->con_priv;
> >> struct drm_encoder *encoder =
> >> radeon_best_single_encoder(connector);
> >> + int r;
> >>
> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> - return connector->status;
> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> + if (r < 0)
> >> + return connector_status_disconnected;
> >> +
> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> + ret = connector->status;
> >> + goto out;
> >> + }
> >>
> >> if (radeon_connector->edid) {
> >> kfree(radeon_connector->edid);
> >> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector *connector,
> >> bool force)
> >> }
> >>
> >> radeon_connector_update_scratch_regs(connector, ret);
> >> +out:
> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> +
> >> return ret;
> >> }
> >>
> >> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
> >> b/drivers/gpu/drm/radeon/radeon_device.c
> >> index 37cfcee..b9b9dfd 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_device.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_device.c
> >> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
> >> "LAST",
> >> };
> >>
> >> +#if defined(CONFIG_VGA_SWITCHEROO)
> >> +bool radeon_is_px(void);
> >> +#else
> >> +static inline bool radeon_is_px(void) { return false; }
> >> +#endif
> >> +
> >> /**
> >> * radeon_program_register_sequence - program an array of registers.
> >> *
> >> @@ -1076,6 +1082,10 @@ static bool
> >> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
> >> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
> >> vga_switcheroo_state state)
> >> {
> >> struct drm_device *dev = pci_get_drvdata(pdev);
> >> +
> >> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
> >> + return;
> >> +
> >> if (state == VGA_SWITCHEROO_ON) {
> >> unsigned d3_delay = dev->pdev->d3_delay;
> >>
> >> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
> >> pci_dev *pdev, enum vga_switchero
> >> if (d3_delay < 20 &&
> >> radeon_switcheroo_quirk_long_wakeup(pdev))
> >> dev->pdev->d3_delay = 20;
> >>
> >> - radeon_resume_kms(dev, 1);
> >> + radeon_resume_kms(dev, true, true);
> >>
> >> dev->pdev->d3_delay = d3_delay;
> >>
> >> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
> >> pci_dev *pdev, enum vga_switchero
> >> printk(KERN_INFO "radeon: switched off\n");
> >> drm_kms_helper_poll_disable(dev);
> >> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> - radeon_suspend_kms(dev, 1);
> >> + radeon_suspend_kms(dev, true, true);
> >> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
> >> }
> >> }
> >> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device *rdev,
> >> {
> >> int r, i;
> >> int dma_bits;
> >> + bool runtime = false;
> >>
> >> rdev->shutdown = false;
> >> rdev->dev = &pdev->dev;
> >> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device
> *rdev,
> >> /* this will fail for cards that aren't VGA class devices, just
> >> * ignore it */
> >> vga_client_register(rdev->pdev, rdev, NULL,
> >> radeon_vga_set_decode);
> >> - vga_switcheroo_register_client(rdev->pdev,
> &radeon_switcheroo_ops,
> >> false);
> >> +
> >> + if (radeon_runtime_pm == 1)
> >> + runtime = true;
> >> + if ((radeon_runtime_pm == -1) && radeon_is_px())
> >> + runtime = true;
> >> + vga_switcheroo_register_client(rdev->pdev,
> &radeon_switcheroo_ops,
> >> runtime);
> >> + if (runtime)
> >> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
> >> &rdev->vga_pm_domain);
> >>
> >> r = radeon_init(rdev);
> >> if (r)
> >> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device
> *rdev)
> >> * Returns 0 for success or an error on failure.
> >> * Called at driver suspend.
> >> */
> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> fbcon)
> >> {
> >> struct radeon_device *rdev;
> >> struct drm_crtc *crtc;
> >> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev,
> bool
> >> suspend)
> >> pci_disable_device(dev->pdev);
> >> pci_set_power_state(dev->pdev, PCI_D3hot);
> >> }
> >> - console_lock();
> >> - radeon_fbdev_set_suspend(rdev, 1);
> >> - console_unlock();
> >> +
> >> + if (fbcon) {
> >> + console_lock();
> >> + radeon_fbdev_set_suspend(rdev, 1);
> >> + console_unlock();
> >> + }
> >> return 0;
> >> }
> >>
> >> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev,
> bool
> >> suspend)
> >> * Returns 0 for success or an error on failure.
> >> * Called at driver resume.
> >> */
> >> -int radeon_resume_kms(struct drm_device *dev, bool resume)
> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
> >> {
> >> struct drm_connector *connector;
> >> struct radeon_device *rdev = dev->dev_private;
> >> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev,
> bool
> >> resume)
> >> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
> >> return 0;
> >>
> >> - console_lock();
> >> + if (fbcon) {
> >> + console_lock();
> >> + }
> >> if (resume) {
> >> pci_set_power_state(dev->pdev, PCI_D0);
> >> pci_restore_state(dev->pdev);
> >> if (pci_enable_device(dev->pdev)) {
> >> - console_unlock();
> >> + if (fbcon)
> >> + console_unlock();
> >> return -1;
> >> }
> >> }
> >> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev,
> bool
> >> resume)
> >> radeon_pm_resume(rdev);
> >> radeon_restore_bios_scratch_regs(rdev);
> >>
> >> - radeon_fbdev_set_suspend(rdev, 0);
> >> - console_unlock();
> >> -
> >> + if (fbcon) {
> >> + radeon_fbdev_set_suspend(rdev, 0);
> >> + console_unlock();
> >> + }
> >> +
> >> /* init dig PHYs, disp eng pll */
> >> if (rdev->is_atom_bios) {
> >> radeon_atom_encoder_init(rdev);
> >> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
> >> b/drivers/gpu/drm/radeon/radeon_display.c
> >> index 0d1aa05..bc37e33 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_display.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> >> @@ -30,6 +30,7 @@
> >> #include "atom.h"
> >> #include <asm/div64.h>
> >>
> >> +#include <linux/pm_runtime.h>
> >> #include <drm/drm_crtc_helper.h>
> >> #include <drm/drm_edid.h>
> >>
> >> @@ -494,11 +495,55 @@ unlock_free:
> >> return r;
> >> }
> >>
> >> +static int
> >> +radeon_crtc_set_config(struct drm_mode_set *set)
> >> +{
> >> + struct drm_device *dev;
> >> + struct radeon_device *rdev;
> >> + struct drm_crtc *crtc;
> >> + bool active = false;
> >> + int ret;
> >> +
> >> + if (!set || !set->crtc)
> >> + return -EINVAL;
> >> +
> >> + dev = set->crtc->dev;
> >> +
> >> + ret = pm_runtime_get_sync(dev->dev);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + ret = drm_crtc_helper_set_config(set);
> >> +
> >> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> >> + if (crtc->enabled)
> >> + active = true;
> >> +
> >> + pm_runtime_mark_last_busy(dev->dev);
> >> +
> >> + rdev = dev->dev_private;
> >> + /* if we have active crtcs and we don't have a power ref,
> >> + take the current one */
> >> + if (active && !rdev->have_disp_power_ref) {
> >> + rdev->have_disp_power_ref = true;
> >> + return ret;
> >> + }
> >> + /* if we have no active crtcs, then drop the power ref
> >> + we got before */
> >> + if (!active && rdev->have_disp_power_ref) {
> >> + pm_runtime_put_autosuspend(dev->dev);
> >> + rdev->have_disp_power_ref = false;
> >> + }
> >> +
> >> + /* drop the power reference we got coming in here */
> >> + pm_runtime_put_autosuspend(dev->dev);
> >> + return ret;
> >> +}
> >> static const struct drm_crtc_funcs radeon_crtc_funcs = {
> >> .cursor_set = radeon_crtc_cursor_set,
> >> .cursor_move = radeon_crtc_cursor_move,
> >> .gamma_set = radeon_crtc_gamma_set,
> >> - .set_config = drm_crtc_helper_set_config,
> >> + .set_config = radeon_crtc_set_config,
> >> .destroy = radeon_crtc_destroy,
> >> .page_flip = radeon_crtc_page_flip,
> >> };
> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
> >> b/drivers/gpu/drm/radeon/radeon_drv.c
> >> index 788bfb0..427c64f 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> >> @@ -36,8 +36,9 @@
> >> #include <drm/drm_pciids.h>
> >> #include <linux/console.h>
> >> #include <linux/module.h>
> >> -
> >> -
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/vga_switcheroo.h>
> >> +#include "drm_crtc_helper.h"
> >> /*
> >> * KMS wrapper.
> >> * - 2.0.0 - initial interface
> >> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device
> *dev,
> >> struct drm_file *file_priv);
> >> void radeon_driver_preclose_kms(struct drm_device *dev,
> >> struct drm_file *file_priv);
> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> -int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> fbcon);
> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
> >> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
> >> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
> >> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
> >> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor
> *minor);
> >> #if defined(CONFIG_VGA_SWITCHEROO)
> >> void radeon_register_atpx_handler(void);
> >> void radeon_unregister_atpx_handler(void);
> >> +bool radeon_is_px(void);
> >> #else
> >> static inline void radeon_register_atpx_handler(void) {}
> >> static inline void radeon_unregister_atpx_handler(void) {}
> >> +static inline bool radeon_is_px(void) { return false; }
> >> #endif
> >>
> >> int radeon_no_wb;
> >> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
> >> int radeon_fastfb = 0;
> >> int radeon_dpm = -1;
> >> int radeon_aspm = -1;
> >> +int radeon_runtime_pm = -1;
> >>
> >> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
> >> module_param_named(no_wb, radeon_no_wb, int, 0444);
> >> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
> >> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
> >> auto)");
> >> module_param_named(aspm, radeon_aspm, int, 0444);
> >>
> >> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable,
> -1
> >> = PX only default)");
> >> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
> >> +
> >> static struct pci_device_id pciidlist[] = {
> >> radeon_PCI_IDS
> >> };
> >> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
> >> return 0;
> >> }
> >>
> >> +
> >> static const struct file_operations radeon_driver_old_fops = {
> >> .owner = THIS_MODULE,
> >> .open = drm_open,
> >> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device
> *dev)
> >> {
> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> - return radeon_suspend_kms(drm_dev, 1);
> >> + return radeon_suspend_kms(drm_dev, true, true);
> >> }
> >>
> >> static int radeon_pmops_resume(struct device *dev)
> >> {
> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> - return radeon_resume_kms(drm_dev, 1);
> >> + return radeon_resume_kms(drm_dev, true, true);
> >> }
> >>
> >> static int radeon_pmops_freeze(struct device *dev)
> >> {
> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> - return radeon_suspend_kms(drm_dev, 0);
> >> + return radeon_suspend_kms(drm_dev, false, true);
> >> }
> >>
> >> static int radeon_pmops_thaw(struct device *dev)
> >> {
> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> - return radeon_resume_kms(drm_dev, 0);
> >> + return radeon_resume_kms(drm_dev, false, true);
> >> +}
> >> +
> >> +static int radeon_pmops_runtime_suspend(struct device *dev)
> >> +{
> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> + int ret;
> >> +
> >> + if (radeon_runtime_pm == 0)
> >> + return -EINVAL;
> >> +
> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> + drm_kms_helper_poll_disable(drm_dev);
> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
> >> +
> >> + ret = radeon_suspend_kms(drm_dev, false, false);
> >> + pci_save_state(pdev);
> >> + pci_disable_device(pdev);
> >> + pci_set_power_state(pdev, PCI_D3cold);
> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int radeon_pmops_runtime_resume(struct device *dev)
> >> +{
> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> + int ret;
> >> +
> >> + if (radeon_runtime_pm == 0)
> >> + return -EINVAL;
> >> +
> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> +
> >> + pci_set_power_state(pdev, PCI_D0);
> >> + pci_restore_state(pdev);
> >> + ret = pci_enable_device(pdev);
> >> + if (ret)
> >> + return ret;
> >> + pci_set_master(pdev);
> >> +
> >> + ret = radeon_resume_kms(drm_dev, false, false);
> >> + drm_kms_helper_poll_enable(drm_dev);
> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
> >> + return 0;
> >> +}
> >> +
> >> +static int radeon_pmops_runtime_idle(struct device *dev)
> >> +{
> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> + struct drm_crtc *crtc;
> >> +
> >> + if (radeon_runtime_pm == 0)
> >> + return -EBUSY;
> >> +
> >> + /* are we PX enabled? */
> >> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
> >> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
> >> + return -EBUSY;
> >> + }
> >> +
> >> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list,
> head) {
> >> + if (crtc->enabled) {
> >> + DRM_DEBUG_DRIVER("failing to power off - crtc
> >> active\n");
> >> + return -EBUSY;
> >> + }
> >> + }
> >> +
> >> + pm_runtime_mark_last_busy(dev);
> >> + pm_runtime_autosuspend(dev);
> >> + /* we don't want the main rpm_idle to call suspend - we want to
> >> autosuspend */
> >> + return 1;
> >> +}
> >> +
> >> +long radeon_drm_ioctl(struct file *filp,
> >> + unsigned int cmd, unsigned long arg)
> >> +{
> >> + struct drm_file *file_priv = filp->private_data;
> >> + struct drm_device *dev;
> >> + long ret;
> >> + dev = file_priv->minor->dev;
> >> + ret = pm_runtime_get_sync(dev->dev);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + ret = drm_ioctl(filp, cmd, arg);
> >> +
> >> + pm_runtime_mark_last_busy(dev->dev);
> >> + pm_runtime_put_autosuspend(dev->dev);
> >> + return ret;
> >> }
> >>
> >> static const struct dev_pm_ops radeon_pm_ops = {
> >> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops = {
> >> .thaw = radeon_pmops_thaw,
> >> .poweroff = radeon_pmops_freeze,
> >> .restore = radeon_pmops_resume,
> >> + .runtime_suspend = radeon_pmops_runtime_suspend,
> >> + .runtime_resume = radeon_pmops_runtime_resume,
> >> + .runtime_idle = radeon_pmops_runtime_idle,
> >> };
> >>
> >> static const struct file_operations radeon_driver_kms_fops = {
> >> .owner = THIS_MODULE,
> >> .open = drm_open,
> >> .release = drm_release,
> >> - .unlocked_ioctl = drm_ioctl,
> >> + .unlocked_ioctl = radeon_drm_ioctl,
> >> .mmap = radeon_mmap,
> >> .poll = drm_poll,
> >> .read = drm_read,
> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
> >> b/drivers/gpu/drm/radeon/radeon_drv.h
> >> index b369d42..543dcfa 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_drv.h
> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
> >> @@ -113,6 +113,9 @@
> >> #define DRIVER_MINOR 33
> >> #define DRIVER_PATCHLEVEL 0
> >>
> >> +long radeon_drm_ioctl(struct file *filp,
> >> + unsigned int cmd, unsigned long arg);
> >> +
> >> /* The rest of the file is DEPRECATED! */
> >> #ifdef CONFIG_DRM_RADEON_UMS
> >>
> >> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> index c180df8..bdb0f93 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
> >> unsigned int cmd, unsigned long
> >> if (nr < DRM_COMMAND_BASE)
> >> return drm_compat_ioctl(filp, cmd, arg);
> >>
> >> - ret = drm_ioctl(filp, cmd, arg);
> >> + ret = radeon_drm_ioctl(filp, cmd, arg);
> >>
> >> return ret;
> >> }
> >> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> index cc9e848..ec6240b 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> @@ -32,6 +32,8 @@
> >> #include "radeon.h"
> >> #include "atom.h"
> >>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> #define RADEON_WAIT_IDLE_TIMEOUT 200
> >>
> >> /**
> >> @@ -47,8 +49,12 @@ irqreturn_t
> radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
> >> {
> >> struct drm_device *dev = (struct drm_device *) arg;
> >> struct radeon_device *rdev = dev->dev_private;
> >> + irqreturn_t ret;
> >>
> >> - return radeon_irq_process(rdev);
> >> + ret = radeon_irq_process(rdev);
> >> + if (ret == IRQ_HANDLED)
> >> + pm_runtime_mark_last_busy(dev->dev);
> >> + return ret;
> >> }
> >>
> >> /*
> >> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
> >> b/drivers/gpu/drm/radeon/radeon_kms.c
> >> index 61580dd..bffff51 100644
> >> --- a/drivers/gpu/drm/radeon/radeon_kms.c
> >> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
> >> @@ -32,7 +32,7 @@
> >>
> >> #include <linux/vga_switcheroo.h>
> >> #include <linux/slab.h>
> >> -
> >> +#include <linux/pm_runtime.h>
> >> /**
> >> * radeon_driver_unload_kms - Main unload function for KMS.
> >> *
> >> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
> >>
> >> if (rdev == NULL)
> >> return 0;
> >> +
> >> if (rdev->rmmio == NULL)
> >> goto done_free;
> >> +
> >> + pm_runtime_get_sync(dev->dev);
> >> +
> >> radeon_acpi_fini(rdev);
> >> +
> >> radeon_modeset_fini(rdev);
> >> radeon_device_fini(rdev);
> >>
> >> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev,
> >> unsigned long flags)
> >> "Error during ACPI methods call\n");
> >> }
> >>
> >> + if (radeon_runtime_pm != 0) {
> >> + pm_runtime_use_autosuspend(dev->dev);
> >> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
> >> + pm_runtime_set_active(dev->dev);
> >> + pm_runtime_allow(dev->dev);
> >> + pm_runtime_mark_last_busy(dev->dev);
> >> + pm_runtime_put_autosuspend(dev->dev);
> >> + }
> >> +
> >> out:
> >> if (r)
> >> radeon_driver_unload_kms(dev);
> >> +
> >> +
> >> return r;
> >> }
> >>
> >> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device
> >> *dev)
> >> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
> >> *file_priv)
> >> {
> >> struct radeon_device *rdev = dev->dev_private;
> >> + int r;
> >>
> >> file_priv->driver_priv = NULL;
> >>
> >> + r = pm_runtime_get_sync(dev->dev);
> >> + if (r < 0)
> >> + return r;
> >> +
> >> /* new gpu have virtual address space support */
> >> if (rdev->family >= CHIP_CAYMAN) {
> >> struct radeon_fpriv *fpriv;
> >> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev,
> >> struct drm_file *file_priv)
> >>
> >> file_priv->driver_priv = fpriv;
> >> }
> >> +
> >> + pm_runtime_mark_last_busy(dev->dev);
> >> + pm_runtime_put_autosuspend(dev->dev);
> >> return 0;
> >> }
> >>
> >> --
> >> 1.8.3.1
> >>
> >> _______________________________________________
> >> dri-devel mailing list
> >> dri-devel@lists.freedesktop.org
> >> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
> >
>
[-- Attachment #1.2: Type: text/html, Size: 42209 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 22:10 ` Mike Lothian
@ 2013-09-20 22:12 ` Alex Deucher
2013-09-20 22:26 ` Mike Lothian
2013-09-20 22:30 ` Mike Lothian
0 siblings, 2 replies; 10+ messages in thread
From: Alex Deucher @ 2013-09-20 22:12 UTC (permalink / raw)
To: Mike Lothian; +Cc: Alex Deucher, Dave Airlie, Maling list - DRI developers
On Fri, Sep 20, 2013 at 6:10 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
> Sorry that was a typo on my part. I'm using radeon.runpm=1
>
> I can see audio is switched off
>
> [drm] Disabling audio 0 support
>
> When I use DRI_PRIME=1 I see the DRM initialisation for my card each time I
> fire up an application or run glxinfo
Yup. that's the card resuming.
>
> There isn't any explicit messages that the card is on of off however
>
> How can I tell if I have a powerxpress card?
If you have a laptop with an integrated GPU and a discrete GPU. You
should see a message about atpx.
Alex
>
> Thanks again
>
> Mike
>
> On 20 Sep 2013 22:05, "Alex Deucher" <alexdeucher@gmail.com> wrote:
>>
>> On Fri, Sep 20, 2013 at 4:25 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
>> > Hi
>> >
>> > Is there an easy way to check this is on?
>>
>> It's on by default if your system is a powerxpress system (hybrid laptop).
>>
>> >
>> > I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
>> > messages saying the card if now off at the moment I can old see messages
>> > saying when the card gets powered up
>> >
>>
>> The option is radeon.runpm for this. Note that only powerxpress
>> systems are supported. There is no support for powering down
>> arbitrary cards yet.
>>
>> > Is it possible to have the on and off messages appearing?
>>
>> On a supported system, you will see suspend and resume messages when
>> when the card is powered down/up.
>>
>> Alex
>>
>> >
>> > Cheers
>> >
>> > Mike
>> >
>> >
>> > On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com> wrote:
>> >>
>> >> From: Dave Airlie <airlied@redhat.com>
>> >>
>> >> This hooks radeon up to the runtime PM system to enable
>> >> dynamic power management for secondary GPUs in switchable
>> >> and powerxpress laptops.
>> >>
>> >> v2: agd5f: clean up, add module parameter
>> >>
>> >> Signed-off-by: Dave Airlie <airlied@redhat.com>
>> >> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
>> >> ---
>> >> drivers/gpu/drm/radeon/radeon.h | 8 +-
>> >> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
>> >> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
>> >> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
>> >> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
>> >> drivers/gpu/drm/radeon/radeon_drv.c | 122
>> >> +++++++++++++++++++++++++--
>> >> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
>> >> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
>> >> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
>> >> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
>> >> 10 files changed, 299 insertions(+), 36 deletions(-)
>> >>
>> >> diff --git a/drivers/gpu/drm/radeon/radeon.h
>> >> b/drivers/gpu/drm/radeon/radeon.h
>> >> index 986100a..ad54525 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon.h
>> >> +++ b/drivers/gpu/drm/radeon/radeon.h
>> >> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
>> >> extern int radeon_fastfb;
>> >> extern int radeon_dpm;
>> >> extern int radeon_aspm;
>> >> +extern int radeon_runtime_pm;
>> >>
>> >> /*
>> >> * Copy from radeon_drv.h so we don't have to include both and have
>> >> conflicting
>> >> @@ -2212,6 +2213,9 @@ struct radeon_device {
>> >> /* clock, powergating flags */
>> >> u32 cg_flags;
>> >> u32 pg_flags;
>> >> +
>> >> + struct dev_pm_domain vga_pm_domain;
>> >> + bool have_disp_power_ref;
>> >> };
>> >>
>> >> int radeon_device_init(struct radeon_device *rdev,
>> >> @@ -2673,8 +2677,8 @@ extern void
>> >> radeon_ttm_placement_from_domain(struct
>> >> radeon_bo *rbo, u32 domain);
>> >> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
>> >> extern void radeon_vram_location(struct radeon_device *rdev, struct
>> >> radeon_mc *mc, u64 base);
>> >> extern void radeon_gtt_location(struct radeon_device *rdev, struct
>> >> radeon_mc *mc);
>> >> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
>> >> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
>> >> +extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool
>> >> fbcon);
>> >> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend,
>> >> bool
>> >> fbcon);
>> >> extern void radeon_ttm_set_active_vram_size(struct radeon_device
>> >> *rdev,
>> >> u64 size);
>> >> extern void radeon_program_register_sequence(struct radeon_device
>> >> *rdev,
>> >> const u32 *registers,
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> >> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> >> index d96070b..6153ec1 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
>> >> @@ -59,6 +59,10 @@ struct atpx_mux {
>> >> u16 mux;
>> >> } __packed;
>> >>
>> >> +bool radeon_is_px(void) {
>> >> + return radeon_atpx_priv.atpx_detected;
>> >> +}
>> >> +
>> >> /**
>> >> * radeon_atpx_call - call an ATPX method
>> >> *
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
>> >> b/drivers/gpu/drm/radeon/radeon_connectors.c
>> >> index 79159b5..5855b5b 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
>> >> @@ -31,6 +31,8 @@
>> >> #include "radeon.h"
>> >> #include "atom.h"
>> >>
>> >> +#include <linux/pm_runtime.h>
>> >> +
>> >> extern void
>> >> radeon_combios_connected_scratch_regs(struct drm_connector *connector,
>> >> struct drm_encoder *encoder,
>> >> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector
>> >> *connector,
>> >> bool force)
>> >> struct radeon_connector *radeon_connector =
>> >> to_radeon_connector(connector);
>> >> struct drm_encoder *encoder =
>> >> radeon_best_single_encoder(connector);
>> >> enum drm_connector_status ret = connector_status_disconnected;
>> >> + int r;
>> >> +
>> >> + r = pm_runtime_get_sync(connector->dev->dev);
>> >> + if (r < 0)
>> >> + return connector_status_disconnected;
>> >>
>> >> if (encoder) {
>> >> struct radeon_encoder *radeon_encoder =
>> >> to_radeon_encoder(encoder);
>> >> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector *connector,
>> >> bool force)
>> >> /* check acpi lid status ??? */
>> >>
>> >> radeon_connector_update_scratch_regs(connector, ret);
>> >> + pm_runtime_mark_last_busy(connector->dev->dev);
>> >> + pm_runtime_put_autosuspend(connector->dev->dev);
>> >> return ret;
>> >> }
>> >>
>> >> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector *connector,
>> >> bool force)
>> >> struct drm_encoder_helper_funcs *encoder_funcs;
>> >> bool dret = false;
>> >> enum drm_connector_status ret = connector_status_disconnected;
>> >> + int r;
>> >> +
>> >> + r = pm_runtime_get_sync(connector->dev->dev);
>> >> + if (r < 0)
>> >> + return connector_status_disconnected;
>> >>
>> >> encoder = radeon_best_single_encoder(connector);
>> >> if (!encoder)
>> >> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector *connector,
>> >> bool force)
>> >> * detected a monitor via load.
>> >> */
>> >> if (radeon_connector->detected_by_load)
>> >> - return connector->status;
>> >> - else
>> >> - return ret;
>> >> + ret = connector->status;
>> >> + goto out;
>> >> }
>> >>
>> >> if (radeon_connector->dac_load_detect && encoder) {
>> >> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector *connector,
>> >> bool force)
>> >> }
>> >>
>> >> radeon_connector_update_scratch_regs(connector, ret);
>> >> +
>> >> +out:
>> >> + pm_runtime_mark_last_busy(connector->dev->dev);
>> >> + pm_runtime_put_autosuspend(connector->dev->dev);
>> >> +
>> >> return ret;
>> >> }
>> >>
>> >> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector *connector,
>> >> bool force)
>> >> struct drm_encoder_helper_funcs *encoder_funcs;
>> >> struct radeon_connector *radeon_connector =
>> >> to_radeon_connector(connector);
>> >> enum drm_connector_status ret = connector_status_disconnected;
>> >> + int r;
>> >>
>> >> if (!radeon_connector->dac_load_detect)
>> >> return ret;
>> >>
>> >> + r = pm_runtime_get_sync(connector->dev->dev);
>> >> + if (r < 0)
>> >> + return connector_status_disconnected;
>> >> +
>> >> encoder = radeon_best_single_encoder(connector);
>> >> if (!encoder)
>> >> ret = connector_status_disconnected;
>> >> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector,
>> >> bool
>> >> force)
>> >> if (ret == connector_status_connected)
>> >> ret =
>> >> radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret,
>> >> false);
>> >> radeon_connector_update_scratch_regs(connector, ret);
>> >> + pm_runtime_mark_last_busy(connector->dev->dev);
>> >> + pm_runtime_put_autosuspend(connector->dev->dev);
>> >> return ret;
>> >> }
>> >>
>> >> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector
>> >> *connector,
>> >> bool force)
>> >> struct drm_encoder *encoder = NULL;
>> >> struct drm_encoder_helper_funcs *encoder_funcs;
>> >> struct drm_mode_object *obj;
>> >> - int i;
>> >> + int i, r;
>> >> enum drm_connector_status ret = connector_status_disconnected;
>> >> bool dret = false, broken_edid = false;
>> >>
>> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
>> >> - return connector->status;
>> >> + r = pm_runtime_get_sync(connector->dev->dev);
>> >> + if (r < 0)
>> >> + return connector_status_disconnected;
>> >> +
>> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
>> >> + ret = connector->status;
>> >> + goto exit;
>> >> + }
>> >>
>> >> if (radeon_connector->ddc_bus)
>> >> dret = radeon_ddc_probe(radeon_connector, false);
>> >> @@ -1110,6 +1141,11 @@ out:
>> >>
>> >> /* updated in get modes as well since we need to know if it's
>> >> analog or digital */
>> >> radeon_connector_update_scratch_regs(connector, ret);
>> >> +
>> >> +exit:
>> >> + pm_runtime_mark_last_busy(connector->dev->dev);
>> >> + pm_runtime_put_autosuspend(connector->dev->dev);
>> >> +
>> >> return ret;
>> >> }
>> >>
>> >> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector
>> >> *connector,
>> >> bool force)
>> >> enum drm_connector_status ret = connector_status_disconnected;
>> >> struct radeon_connector_atom_dig *radeon_dig_connector =
>> >> radeon_connector->con_priv;
>> >> struct drm_encoder *encoder =
>> >> radeon_best_single_encoder(connector);
>> >> + int r;
>> >>
>> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
>> >> - return connector->status;
>> >> + r = pm_runtime_get_sync(connector->dev->dev);
>> >> + if (r < 0)
>> >> + return connector_status_disconnected;
>> >> +
>> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
>> >> + ret = connector->status;
>> >> + goto out;
>> >> + }
>> >>
>> >> if (radeon_connector->edid) {
>> >> kfree(radeon_connector->edid);
>> >> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector
>> >> *connector,
>> >> bool force)
>> >> }
>> >>
>> >> radeon_connector_update_scratch_regs(connector, ret);
>> >> +out:
>> >> + pm_runtime_mark_last_busy(connector->dev->dev);
>> >> + pm_runtime_put_autosuspend(connector->dev->dev);
>> >> +
>> >> return ret;
>> >> }
>> >>
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
>> >> b/drivers/gpu/drm/radeon/radeon_device.c
>> >> index 37cfcee..b9b9dfd 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_device.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_device.c
>> >> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
>> >> "LAST",
>> >> };
>> >>
>> >> +#if defined(CONFIG_VGA_SWITCHEROO)
>> >> +bool radeon_is_px(void);
>> >> +#else
>> >> +static inline bool radeon_is_px(void) { return false; }
>> >> +#endif
>> >> +
>> >> /**
>> >> * radeon_program_register_sequence - program an array of registers.
>> >> *
>> >> @@ -1076,6 +1082,10 @@ static bool
>> >> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
>> >> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
>> >> vga_switcheroo_state state)
>> >> {
>> >> struct drm_device *dev = pci_get_drvdata(pdev);
>> >> +
>> >> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
>> >> + return;
>> >> +
>> >> if (state == VGA_SWITCHEROO_ON) {
>> >> unsigned d3_delay = dev->pdev->d3_delay;
>> >>
>> >> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
>> >> pci_dev *pdev, enum vga_switchero
>> >> if (d3_delay < 20 &&
>> >> radeon_switcheroo_quirk_long_wakeup(pdev))
>> >> dev->pdev->d3_delay = 20;
>> >>
>> >> - radeon_resume_kms(dev, 1);
>> >> + radeon_resume_kms(dev, true, true);
>> >>
>> >> dev->pdev->d3_delay = d3_delay;
>> >>
>> >> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
>> >> pci_dev *pdev, enum vga_switchero
>> >> printk(KERN_INFO "radeon: switched off\n");
>> >> drm_kms_helper_poll_disable(dev);
>> >> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> >> - radeon_suspend_kms(dev, 1);
>> >> + radeon_suspend_kms(dev, true, true);
>> >> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
>> >> }
>> >> }
>> >> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device
>> >> *rdev,
>> >> {
>> >> int r, i;
>> >> int dma_bits;
>> >> + bool runtime = false;
>> >>
>> >> rdev->shutdown = false;
>> >> rdev->dev = &pdev->dev;
>> >> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device
>> >> *rdev,
>> >> /* this will fail for cards that aren't VGA class devices, just
>> >> * ignore it */
>> >> vga_client_register(rdev->pdev, rdev, NULL,
>> >> radeon_vga_set_decode);
>> >> - vga_switcheroo_register_client(rdev->pdev,
>> >> &radeon_switcheroo_ops,
>> >> false);
>> >> +
>> >> + if (radeon_runtime_pm == 1)
>> >> + runtime = true;
>> >> + if ((radeon_runtime_pm == -1) && radeon_is_px())
>> >> + runtime = true;
>> >> + vga_switcheroo_register_client(rdev->pdev,
>> >> &radeon_switcheroo_ops,
>> >> runtime);
>> >> + if (runtime)
>> >> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
>> >> &rdev->vga_pm_domain);
>> >>
>> >> r = radeon_init(rdev);
>> >> if (r)
>> >> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device
>> >> *rdev)
>> >> * Returns 0 for success or an error on failure.
>> >> * Called at driver suspend.
>> >> */
>> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
>> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
>> >> fbcon)
>> >> {
>> >> struct radeon_device *rdev;
>> >> struct drm_crtc *crtc;
>> >> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev,
>> >> bool
>> >> suspend)
>> >> pci_disable_device(dev->pdev);
>> >> pci_set_power_state(dev->pdev, PCI_D3hot);
>> >> }
>> >> - console_lock();
>> >> - radeon_fbdev_set_suspend(rdev, 1);
>> >> - console_unlock();
>> >> +
>> >> + if (fbcon) {
>> >> + console_lock();
>> >> + radeon_fbdev_set_suspend(rdev, 1);
>> >> + console_unlock();
>> >> + }
>> >> return 0;
>> >> }
>> >>
>> >> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev,
>> >> bool
>> >> suspend)
>> >> * Returns 0 for success or an error on failure.
>> >> * Called at driver resume.
>> >> */
>> >> -int radeon_resume_kms(struct drm_device *dev, bool resume)
>> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
>> >> {
>> >> struct drm_connector *connector;
>> >> struct radeon_device *rdev = dev->dev_private;
>> >> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev,
>> >> bool
>> >> resume)
>> >> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
>> >> return 0;
>> >>
>> >> - console_lock();
>> >> + if (fbcon) {
>> >> + console_lock();
>> >> + }
>> >> if (resume) {
>> >> pci_set_power_state(dev->pdev, PCI_D0);
>> >> pci_restore_state(dev->pdev);
>> >> if (pci_enable_device(dev->pdev)) {
>> >> - console_unlock();
>> >> + if (fbcon)
>> >> + console_unlock();
>> >> return -1;
>> >> }
>> >> }
>> >> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev,
>> >> bool
>> >> resume)
>> >> radeon_pm_resume(rdev);
>> >> radeon_restore_bios_scratch_regs(rdev);
>> >>
>> >> - radeon_fbdev_set_suspend(rdev, 0);
>> >> - console_unlock();
>> >> -
>> >> + if (fbcon) {
>> >> + radeon_fbdev_set_suspend(rdev, 0);
>> >> + console_unlock();
>> >> + }
>> >> +
>> >> /* init dig PHYs, disp eng pll */
>> >> if (rdev->is_atom_bios) {
>> >> radeon_atom_encoder_init(rdev);
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
>> >> b/drivers/gpu/drm/radeon/radeon_display.c
>> >> index 0d1aa05..bc37e33 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_display.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_display.c
>> >> @@ -30,6 +30,7 @@
>> >> #include "atom.h"
>> >> #include <asm/div64.h>
>> >>
>> >> +#include <linux/pm_runtime.h>
>> >> #include <drm/drm_crtc_helper.h>
>> >> #include <drm/drm_edid.h>
>> >>
>> >> @@ -494,11 +495,55 @@ unlock_free:
>> >> return r;
>> >> }
>> >>
>> >> +static int
>> >> +radeon_crtc_set_config(struct drm_mode_set *set)
>> >> +{
>> >> + struct drm_device *dev;
>> >> + struct radeon_device *rdev;
>> >> + struct drm_crtc *crtc;
>> >> + bool active = false;
>> >> + int ret;
>> >> +
>> >> + if (!set || !set->crtc)
>> >> + return -EINVAL;
>> >> +
>> >> + dev = set->crtc->dev;
>> >> +
>> >> + ret = pm_runtime_get_sync(dev->dev);
>> >> + if (ret < 0)
>> >> + return ret;
>> >> +
>> >> + ret = drm_crtc_helper_set_config(set);
>> >> +
>> >> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>> >> + if (crtc->enabled)
>> >> + active = true;
>> >> +
>> >> + pm_runtime_mark_last_busy(dev->dev);
>> >> +
>> >> + rdev = dev->dev_private;
>> >> + /* if we have active crtcs and we don't have a power ref,
>> >> + take the current one */
>> >> + if (active && !rdev->have_disp_power_ref) {
>> >> + rdev->have_disp_power_ref = true;
>> >> + return ret;
>> >> + }
>> >> + /* if we have no active crtcs, then drop the power ref
>> >> + we got before */
>> >> + if (!active && rdev->have_disp_power_ref) {
>> >> + pm_runtime_put_autosuspend(dev->dev);
>> >> + rdev->have_disp_power_ref = false;
>> >> + }
>> >> +
>> >> + /* drop the power reference we got coming in here */
>> >> + pm_runtime_put_autosuspend(dev->dev);
>> >> + return ret;
>> >> +}
>> >> static const struct drm_crtc_funcs radeon_crtc_funcs = {
>> >> .cursor_set = radeon_crtc_cursor_set,
>> >> .cursor_move = radeon_crtc_cursor_move,
>> >> .gamma_set = radeon_crtc_gamma_set,
>> >> - .set_config = drm_crtc_helper_set_config,
>> >> + .set_config = radeon_crtc_set_config,
>> >> .destroy = radeon_crtc_destroy,
>> >> .page_flip = radeon_crtc_page_flip,
>> >> };
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
>> >> b/drivers/gpu/drm/radeon/radeon_drv.c
>> >> index 788bfb0..427c64f 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_drv.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
>> >> @@ -36,8 +36,9 @@
>> >> #include <drm/drm_pciids.h>
>> >> #include <linux/console.h>
>> >> #include <linux/module.h>
>> >> -
>> >> -
>> >> +#include <linux/pm_runtime.h>
>> >> +#include <linux/vga_switcheroo.h>
>> >> +#include "drm_crtc_helper.h"
>> >> /*
>> >> * KMS wrapper.
>> >> * - 2.0.0 - initial interface
>> >> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device
>> >> *dev,
>> >> struct drm_file *file_priv);
>> >> void radeon_driver_preclose_kms(struct drm_device *dev,
>> >> struct drm_file *file_priv);
>> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
>> >> -int radeon_resume_kms(struct drm_device *dev, bool resume);
>> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
>> >> fbcon);
>> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool
>> >> fbcon);
>> >> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
>> >> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
>> >> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
>> >> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor
>> >> *minor);
>> >> #if defined(CONFIG_VGA_SWITCHEROO)
>> >> void radeon_register_atpx_handler(void);
>> >> void radeon_unregister_atpx_handler(void);
>> >> +bool radeon_is_px(void);
>> >> #else
>> >> static inline void radeon_register_atpx_handler(void) {}
>> >> static inline void radeon_unregister_atpx_handler(void) {}
>> >> +static inline bool radeon_is_px(void) { return false; }
>> >> #endif
>> >>
>> >> int radeon_no_wb;
>> >> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
>> >> int radeon_fastfb = 0;
>> >> int radeon_dpm = -1;
>> >> int radeon_aspm = -1;
>> >> +int radeon_runtime_pm = -1;
>> >>
>> >> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch
>> >> registers");
>> >> module_param_named(no_wb, radeon_no_wb, int, 0444);
>> >> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
>> >> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
>> >> auto)");
>> >> module_param_named(aspm, radeon_aspm, int, 0444);
>> >>
>> >> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable,
>> >> -1
>> >> = PX only default)");
>> >> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
>> >> +
>> >> static struct pci_device_id pciidlist[] = {
>> >> radeon_PCI_IDS
>> >> };
>> >> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
>> >> return 0;
>> >> }
>> >>
>> >> +
>> >> static const struct file_operations radeon_driver_old_fops = {
>> >> .owner = THIS_MODULE,
>> >> .open = drm_open,
>> >> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device
>> >> *dev)
>> >> {
>> >> struct pci_dev *pdev = to_pci_dev(dev);
>> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> - return radeon_suspend_kms(drm_dev, 1);
>> >> + return radeon_suspend_kms(drm_dev, true, true);
>> >> }
>> >>
>> >> static int radeon_pmops_resume(struct device *dev)
>> >> {
>> >> struct pci_dev *pdev = to_pci_dev(dev);
>> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> - return radeon_resume_kms(drm_dev, 1);
>> >> + return radeon_resume_kms(drm_dev, true, true);
>> >> }
>> >>
>> >> static int radeon_pmops_freeze(struct device *dev)
>> >> {
>> >> struct pci_dev *pdev = to_pci_dev(dev);
>> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> - return radeon_suspend_kms(drm_dev, 0);
>> >> + return radeon_suspend_kms(drm_dev, false, true);
>> >> }
>> >>
>> >> static int radeon_pmops_thaw(struct device *dev)
>> >> {
>> >> struct pci_dev *pdev = to_pci_dev(dev);
>> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> - return radeon_resume_kms(drm_dev, 0);
>> >> + return radeon_resume_kms(drm_dev, false, true);
>> >> +}
>> >> +
>> >> +static int radeon_pmops_runtime_suspend(struct device *dev)
>> >> +{
>> >> + struct pci_dev *pdev = to_pci_dev(dev);
>> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> + int ret;
>> >> +
>> >> + if (radeon_runtime_pm == 0)
>> >> + return -EINVAL;
>> >> +
>> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> >> + drm_kms_helper_poll_disable(drm_dev);
>> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
>> >> +
>> >> + ret = radeon_suspend_kms(drm_dev, false, false);
>> >> + pci_save_state(pdev);
>> >> + pci_disable_device(pdev);
>> >> + pci_set_power_state(pdev, PCI_D3cold);
>> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int radeon_pmops_runtime_resume(struct device *dev)
>> >> +{
>> >> + struct pci_dev *pdev = to_pci_dev(dev);
>> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> + int ret;
>> >> +
>> >> + if (radeon_runtime_pm == 0)
>> >> + return -EINVAL;
>> >> +
>> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
>> >> +
>> >> + pci_set_power_state(pdev, PCI_D0);
>> >> + pci_restore_state(pdev);
>> >> + ret = pci_enable_device(pdev);
>> >> + if (ret)
>> >> + return ret;
>> >> + pci_set_master(pdev);
>> >> +
>> >> + ret = radeon_resume_kms(drm_dev, false, false);
>> >> + drm_kms_helper_poll_enable(drm_dev);
>> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
>> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int radeon_pmops_runtime_idle(struct device *dev)
>> >> +{
>> >> + struct pci_dev *pdev = to_pci_dev(dev);
>> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
>> >> + struct drm_crtc *crtc;
>> >> +
>> >> + if (radeon_runtime_pm == 0)
>> >> + return -EBUSY;
>> >> +
>> >> + /* are we PX enabled? */
>> >> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
>> >> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
>> >> + return -EBUSY;
>> >> + }
>> >> +
>> >> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list,
>> >> head) {
>> >> + if (crtc->enabled) {
>> >> + DRM_DEBUG_DRIVER("failing to power off - crtc
>> >> active\n");
>> >> + return -EBUSY;
>> >> + }
>> >> + }
>> >> +
>> >> + pm_runtime_mark_last_busy(dev);
>> >> + pm_runtime_autosuspend(dev);
>> >> + /* we don't want the main rpm_idle to call suspend - we want to
>> >> autosuspend */
>> >> + return 1;
>> >> +}
>> >> +
>> >> +long radeon_drm_ioctl(struct file *filp,
>> >> + unsigned int cmd, unsigned long arg)
>> >> +{
>> >> + struct drm_file *file_priv = filp->private_data;
>> >> + struct drm_device *dev;
>> >> + long ret;
>> >> + dev = file_priv->minor->dev;
>> >> + ret = pm_runtime_get_sync(dev->dev);
>> >> + if (ret < 0)
>> >> + return ret;
>> >> +
>> >> + ret = drm_ioctl(filp, cmd, arg);
>> >> +
>> >> + pm_runtime_mark_last_busy(dev->dev);
>> >> + pm_runtime_put_autosuspend(dev->dev);
>> >> + return ret;
>> >> }
>> >>
>> >> static const struct dev_pm_ops radeon_pm_ops = {
>> >> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops = {
>> >> .thaw = radeon_pmops_thaw,
>> >> .poweroff = radeon_pmops_freeze,
>> >> .restore = radeon_pmops_resume,
>> >> + .runtime_suspend = radeon_pmops_runtime_suspend,
>> >> + .runtime_resume = radeon_pmops_runtime_resume,
>> >> + .runtime_idle = radeon_pmops_runtime_idle,
>> >> };
>> >>
>> >> static const struct file_operations radeon_driver_kms_fops = {
>> >> .owner = THIS_MODULE,
>> >> .open = drm_open,
>> >> .release = drm_release,
>> >> - .unlocked_ioctl = drm_ioctl,
>> >> + .unlocked_ioctl = radeon_drm_ioctl,
>> >> .mmap = radeon_mmap,
>> >> .poll = drm_poll,
>> >> .read = drm_read,
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
>> >> b/drivers/gpu/drm/radeon/radeon_drv.h
>> >> index b369d42..543dcfa 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_drv.h
>> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
>> >> @@ -113,6 +113,9 @@
>> >> #define DRIVER_MINOR 33
>> >> #define DRIVER_PATCHLEVEL 0
>> >>
>> >> +long radeon_drm_ioctl(struct file *filp,
>> >> + unsigned int cmd, unsigned long arg);
>> >> +
>> >> /* The rest of the file is DEPRECATED! */
>> >> #ifdef CONFIG_DRM_RADEON_UMS
>> >>
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
>> >> b/drivers/gpu/drm/radeon/radeon_ioc32.c
>> >> index c180df8..bdb0f93 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
>> >> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
>> >> unsigned int cmd, unsigned long
>> >> if (nr < DRM_COMMAND_BASE)
>> >> return drm_compat_ioctl(filp, cmd, arg);
>> >>
>> >> - ret = drm_ioctl(filp, cmd, arg);
>> >> + ret = radeon_drm_ioctl(filp, cmd, arg);
>> >>
>> >> return ret;
>> >> }
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> >> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> >> index cc9e848..ec6240b 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
>> >> @@ -32,6 +32,8 @@
>> >> #include "radeon.h"
>> >> #include "atom.h"
>> >>
>> >> +#include <linux/pm_runtime.h>
>> >> +
>> >> #define RADEON_WAIT_IDLE_TIMEOUT 200
>> >>
>> >> /**
>> >> @@ -47,8 +49,12 @@ irqreturn_t
>> >> radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
>> >> {
>> >> struct drm_device *dev = (struct drm_device *) arg;
>> >> struct radeon_device *rdev = dev->dev_private;
>> >> + irqreturn_t ret;
>> >>
>> >> - return radeon_irq_process(rdev);
>> >> + ret = radeon_irq_process(rdev);
>> >> + if (ret == IRQ_HANDLED)
>> >> + pm_runtime_mark_last_busy(dev->dev);
>> >> + return ret;
>> >> }
>> >>
>> >> /*
>> >> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
>> >> b/drivers/gpu/drm/radeon/radeon_kms.c
>> >> index 61580dd..bffff51 100644
>> >> --- a/drivers/gpu/drm/radeon/radeon_kms.c
>> >> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
>> >> @@ -32,7 +32,7 @@
>> >>
>> >> #include <linux/vga_switcheroo.h>
>> >> #include <linux/slab.h>
>> >> -
>> >> +#include <linux/pm_runtime.h>
>> >> /**
>> >> * radeon_driver_unload_kms - Main unload function for KMS.
>> >> *
>> >> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device *dev)
>> >>
>> >> if (rdev == NULL)
>> >> return 0;
>> >> +
>> >> if (rdev->rmmio == NULL)
>> >> goto done_free;
>> >> +
>> >> + pm_runtime_get_sync(dev->dev);
>> >> +
>> >> radeon_acpi_fini(rdev);
>> >> +
>> >> radeon_modeset_fini(rdev);
>> >> radeon_device_fini(rdev);
>> >>
>> >> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device *dev,
>> >> unsigned long flags)
>> >> "Error during ACPI methods call\n");
>> >> }
>> >>
>> >> + if (radeon_runtime_pm != 0) {
>> >> + pm_runtime_use_autosuspend(dev->dev);
>> >> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
>> >> + pm_runtime_set_active(dev->dev);
>> >> + pm_runtime_allow(dev->dev);
>> >> + pm_runtime_mark_last_busy(dev->dev);
>> >> + pm_runtime_put_autosuspend(dev->dev);
>> >> + }
>> >> +
>> >> out:
>> >> if (r)
>> >> radeon_driver_unload_kms(dev);
>> >> +
>> >> +
>> >> return r;
>> >> }
>> >>
>> >> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct drm_device
>> >> *dev)
>> >> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
>> >> *file_priv)
>> >> {
>> >> struct radeon_device *rdev = dev->dev_private;
>> >> + int r;
>> >>
>> >> file_priv->driver_priv = NULL;
>> >>
>> >> + r = pm_runtime_get_sync(dev->dev);
>> >> + if (r < 0)
>> >> + return r;
>> >> +
>> >> /* new gpu have virtual address space support */
>> >> if (rdev->family >= CHIP_CAYMAN) {
>> >> struct radeon_fpriv *fpriv;
>> >> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device *dev,
>> >> struct drm_file *file_priv)
>> >>
>> >> file_priv->driver_priv = fpriv;
>> >> }
>> >> +
>> >> + pm_runtime_mark_last_busy(dev->dev);
>> >> + pm_runtime_put_autosuspend(dev->dev);
>> >> return 0;
>> >> }
>> >>
>> >> --
>> >> 1.8.3.1
>> >>
>> >> _______________________________________________
>> >> dri-devel mailing list
>> >> dri-devel@lists.freedesktop.org
>> >> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>> >
>> >
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 22:12 ` Alex Deucher
@ 2013-09-20 22:26 ` Mike Lothian
2013-09-20 22:30 ` Mike Lothian
1 sibling, 0 replies; 10+ messages in thread
From: Mike Lothian @ 2013-09-20 22:26 UTC (permalink / raw)
To: Alex Deucher; +Cc: Alex Deucher, Dave Airlie, Maling list - DRI developers
[-- Attachment #1.1: Type: text/plain, Size: 36624 bytes --]
It's probably easier if I just show you this:
http://pastebin.com/xpBJkZDw
Is that expected behaviour? I'm used to seeing something more definite when
echoing OFF into /sys/kernel/debug/vgaswitcheroo/switch
I've just confirmed with powertop that the laptop is using 26 watts when
idle and 47 watts with DRI_PRIME=1 glxgears running
On 20 September 2013 22:12, Alex Deucher <alexdeucher@gmail.com> wrote:
> On Fri, Sep 20, 2013 at 6:10 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
> > Sorry that was a typo on my part. I'm using radeon.runpm=1
> >
> > I can see audio is switched off
> >
> > [drm] Disabling audio 0 support
> >
> > When I use DRI_PRIME=1 I see the DRM initialisation for my card each
> time I
> > fire up an application or run glxinfo
>
> Yup. that's the card resuming.
>
> >
> > There isn't any explicit messages that the card is on of off however
> >
> > How can I tell if I have a powerxpress card?
>
> If you have a laptop with an integrated GPU and a discrete GPU. You
> should see a message about atpx.
>
> Alex
>
> >
> > Thanks again
> >
> > Mike
> >
> > On 20 Sep 2013 22:05, "Alex Deucher" <alexdeucher@gmail.com> wrote:
> >>
> >> On Fri, Sep 20, 2013 at 4:25 PM, Mike Lothian <mike@fireburn.co.uk>
> wrote:
> >> > Hi
> >> >
> >> > Is there an easy way to check this is on?
> >>
> >> It's on by default if your system is a powerxpress system (hybrid
> laptop).
> >>
> >> >
> >> > I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
> >> > messages saying the card if now off at the moment I can old see
> messages
> >> > saying when the card gets powered up
> >> >
> >>
> >> The option is radeon.runpm for this. Note that only powerxpress
> >> systems are supported. There is no support for powering down
> >> arbitrary cards yet.
> >>
> >> > Is it possible to have the on and off messages appearing?
> >>
> >> On a supported system, you will see suspend and resume messages when
> >> when the card is powered down/up.
> >>
> >> Alex
> >>
> >> >
> >> > Cheers
> >> >
> >> > Mike
> >> >
> >> >
> >> > On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com>
> wrote:
> >> >>
> >> >> From: Dave Airlie <airlied@redhat.com>
> >> >>
> >> >> This hooks radeon up to the runtime PM system to enable
> >> >> dynamic power management for secondary GPUs in switchable
> >> >> and powerxpress laptops.
> >> >>
> >> >> v2: agd5f: clean up, add module parameter
> >> >>
> >> >> Signed-off-by: Dave Airlie <airlied@redhat.com>
> >> >> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
> >> >> ---
> >> >> drivers/gpu/drm/radeon/radeon.h | 8 +-
> >> >> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
> >> >> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
> >> >> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
> >> >> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
> >> >> drivers/gpu/drm/radeon/radeon_drv.c | 122
> >> >> +++++++++++++++++++++++++--
> >> >> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
> >> >> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
> >> >> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
> >> >> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
> >> >> 10 files changed, 299 insertions(+), 36 deletions(-)
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon.h
> >> >> b/drivers/gpu/drm/radeon/radeon.h
> >> >> index 986100a..ad54525 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon.h
> >> >> +++ b/drivers/gpu/drm/radeon/radeon.h
> >> >> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
> >> >> extern int radeon_fastfb;
> >> >> extern int radeon_dpm;
> >> >> extern int radeon_aspm;
> >> >> +extern int radeon_runtime_pm;
> >> >>
> >> >> /*
> >> >> * Copy from radeon_drv.h so we don't have to include both and have
> >> >> conflicting
> >> >> @@ -2212,6 +2213,9 @@ struct radeon_device {
> >> >> /* clock, powergating flags */
> >> >> u32 cg_flags;
> >> >> u32 pg_flags;
> >> >> +
> >> >> + struct dev_pm_domain vga_pm_domain;
> >> >> + bool have_disp_power_ref;
> >> >> };
> >> >>
> >> >> int radeon_device_init(struct radeon_device *rdev,
> >> >> @@ -2673,8 +2677,8 @@ extern void
> >> >> radeon_ttm_placement_from_domain(struct
> >> >> radeon_bo *rbo, u32 domain);
> >> >> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object
> *bo);
> >> >> extern void radeon_vram_location(struct radeon_device *rdev, struct
> >> >> radeon_mc *mc, u64 base);
> >> >> extern void radeon_gtt_location(struct radeon_device *rdev, struct
> >> >> radeon_mc *mc);
> >> >> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> >> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> >> +extern int radeon_resume_kms(struct drm_device *dev, bool resume,
> bool
> >> >> fbcon);
> >> >> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend,
> >> >> bool
> >> >> fbcon);
> >> >> extern void radeon_ttm_set_active_vram_size(struct radeon_device
> >> >> *rdev,
> >> >> u64 size);
> >> >> extern void radeon_program_register_sequence(struct radeon_device
> >> >> *rdev,
> >> >> const u32 *registers,
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> index d96070b..6153ec1 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> @@ -59,6 +59,10 @@ struct atpx_mux {
> >> >> u16 mux;
> >> >> } __packed;
> >> >>
> >> >> +bool radeon_is_px(void) {
> >> >> + return radeon_atpx_priv.atpx_detected;
> >> >> +}
> >> >> +
> >> >> /**
> >> >> * radeon_atpx_call - call an ATPX method
> >> >> *
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> index 79159b5..5855b5b 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> @@ -31,6 +31,8 @@
> >> >> #include "radeon.h"
> >> >> #include "atom.h"
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> +
> >> >> extern void
> >> >> radeon_combios_connected_scratch_regs(struct drm_connector
> *connector,
> >> >> struct drm_encoder *encoder,
> >> >> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> struct radeon_connector *radeon_connector =
> >> >> to_radeon_connector(connector);
> >> >> struct drm_encoder *encoder =
> >> >> radeon_best_single_encoder(connector);
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >> +
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >>
> >> >> if (encoder) {
> >> >> struct radeon_encoder *radeon_encoder =
> >> >> to_radeon_encoder(encoder);
> >> >> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> /* check acpi lid status ??? */
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> bool dret = false;
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >> +
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >>
> >> >> encoder = radeon_best_single_encoder(connector);
> >> >> if (!encoder)
> >> >> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> * detected a monitor via load.
> >> >> */
> >> >> if (radeon_connector->detected_by_load)
> >> >> - return connector->status;
> >> >> - else
> >> >> - return ret;
> >> >> + ret = connector->status;
> >> >> + goto out;
> >> >> }
> >> >>
> >> >> if (radeon_connector->dac_load_detect && encoder) {
> >> >> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> }
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +
> >> >> +out:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> struct radeon_connector *radeon_connector =
> >> >> to_radeon_connector(connector);
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >>
> >> >> if (!radeon_connector->dac_load_detect)
> >> >> return ret;
> >> >>
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> encoder = radeon_best_single_encoder(connector);
> >> >> if (!encoder)
> >> >> ret = connector_status_disconnected;
> >> >> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector,
> >> >> bool
> >> >> force)
> >> >> if (ret == connector_status_connected)
> >> >> ret =
> >> >> radeon_connector_analog_encoder_conflict_solve(connector, encoder,
> ret,
> >> >> false);
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> struct drm_encoder *encoder = NULL;
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> struct drm_mode_object *obj;
> >> >> - int i;
> >> >> + int i, r;
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> bool dret = false, broken_edid = false;
> >> >>
> >> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> >> - return connector->status;
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> >> + ret = connector->status;
> >> >> + goto exit;
> >> >> + }
> >> >>
> >> >> if (radeon_connector->ddc_bus)
> >> >> dret = radeon_ddc_probe(radeon_connector, false);
> >> >> @@ -1110,6 +1141,11 @@ out:
> >> >>
> >> >> /* updated in get modes as well since we need to know if it's
> >> >> analog or digital */
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +
> >> >> +exit:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> struct radeon_connector_atom_dig *radeon_dig_connector =
> >> >> radeon_connector->con_priv;
> >> >> struct drm_encoder *encoder =
> >> >> radeon_best_single_encoder(connector);
> >> >> + int r;
> >> >>
> >> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> >> - return connector->status;
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> >> + ret = connector->status;
> >> >> + goto out;
> >> >> + }
> >> >>
> >> >> if (radeon_connector->edid) {
> >> >> kfree(radeon_connector->edid);
> >> >> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> }
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +out:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
> >> >> b/drivers/gpu/drm/radeon/radeon_device.c
> >> >> index 37cfcee..b9b9dfd 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_device.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_device.c
> >> >> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
> >> >> "LAST",
> >> >> };
> >> >>
> >> >> +#if defined(CONFIG_VGA_SWITCHEROO)
> >> >> +bool radeon_is_px(void);
> >> >> +#else
> >> >> +static inline bool radeon_is_px(void) { return false; }
> >> >> +#endif
> >> >> +
> >> >> /**
> >> >> * radeon_program_register_sequence - program an array of registers.
> >> >> *
> >> >> @@ -1076,6 +1082,10 @@ static bool
> >> >> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
> >> >> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
> >> >> vga_switcheroo_state state)
> >> >> {
> >> >> struct drm_device *dev = pci_get_drvdata(pdev);
> >> >> +
> >> >> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
> >> >> + return;
> >> >> +
> >> >> if (state == VGA_SWITCHEROO_ON) {
> >> >> unsigned d3_delay = dev->pdev->d3_delay;
> >> >>
> >> >> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
> >> >> pci_dev *pdev, enum vga_switchero
> >> >> if (d3_delay < 20 &&
> >> >> radeon_switcheroo_quirk_long_wakeup(pdev))
> >> >> dev->pdev->d3_delay = 20;
> >> >>
> >> >> - radeon_resume_kms(dev, 1);
> >> >> + radeon_resume_kms(dev, true, true);
> >> >>
> >> >> dev->pdev->d3_delay = d3_delay;
> >> >>
> >> >> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
> >> >> pci_dev *pdev, enum vga_switchero
> >> >> printk(KERN_INFO "radeon: switched off\n");
> >> >> drm_kms_helper_poll_disable(dev);
> >> >> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> - radeon_suspend_kms(dev, 1);
> >> >> + radeon_suspend_kms(dev, true, true);
> >> >> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
> >> >> }
> >> >> }
> >> >> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device
> >> >> *rdev,
> >> >> {
> >> >> int r, i;
> >> >> int dma_bits;
> >> >> + bool runtime = false;
> >> >>
> >> >> rdev->shutdown = false;
> >> >> rdev->dev = &pdev->dev;
> >> >> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device
> >> >> *rdev,
> >> >> /* this will fail for cards that aren't VGA class devices,
> just
> >> >> * ignore it */
> >> >> vga_client_register(rdev->pdev, rdev, NULL,
> >> >> radeon_vga_set_decode);
> >> >> - vga_switcheroo_register_client(rdev->pdev,
> >> >> &radeon_switcheroo_ops,
> >> >> false);
> >> >> +
> >> >> + if (radeon_runtime_pm == 1)
> >> >> + runtime = true;
> >> >> + if ((radeon_runtime_pm == -1) && radeon_is_px())
> >> >> + runtime = true;
> >> >> + vga_switcheroo_register_client(rdev->pdev,
> >> >> &radeon_switcheroo_ops,
> >> >> runtime);
> >> >> + if (runtime)
> >> >> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
> >> >> &rdev->vga_pm_domain);
> >> >>
> >> >> r = radeon_init(rdev);
> >> >> if (r)
> >> >> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device
> >> >> *rdev)
> >> >> * Returns 0 for success or an error on failure.
> >> >> * Called at driver suspend.
> >> >> */
> >> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
> >> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> >> >> fbcon)
> >> >> {
> >> >> struct radeon_device *rdev;
> >> >> struct drm_crtc *crtc;
> >> >> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev,
> >> >> bool
> >> >> suspend)
> >> >> pci_disable_device(dev->pdev);
> >> >> pci_set_power_state(dev->pdev, PCI_D3hot);
> >> >> }
> >> >> - console_lock();
> >> >> - radeon_fbdev_set_suspend(rdev, 1);
> >> >> - console_unlock();
> >> >> +
> >> >> + if (fbcon) {
> >> >> + console_lock();
> >> >> + radeon_fbdev_set_suspend(rdev, 1);
> >> >> + console_unlock();
> >> >> + }
> >> >> return 0;
> >> >> }
> >> >>
> >> >> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev,
> >> >> bool
> >> >> suspend)
> >> >> * Returns 0 for success or an error on failure.
> >> >> * Called at driver resume.
> >> >> */
> >> >> -int radeon_resume_kms(struct drm_device *dev, bool resume)
> >> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> fbcon)
> >> >> {
> >> >> struct drm_connector *connector;
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev,
> >> >> bool
> >> >> resume)
> >> >> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
> >> >> return 0;
> >> >>
> >> >> - console_lock();
> >> >> + if (fbcon) {
> >> >> + console_lock();
> >> >> + }
> >> >> if (resume) {
> >> >> pci_set_power_state(dev->pdev, PCI_D0);
> >> >> pci_restore_state(dev->pdev);
> >> >> if (pci_enable_device(dev->pdev)) {
> >> >> - console_unlock();
> >> >> + if (fbcon)
> >> >> + console_unlock();
> >> >> return -1;
> >> >> }
> >> >> }
> >> >> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev,
> >> >> bool
> >> >> resume)
> >> >> radeon_pm_resume(rdev);
> >> >> radeon_restore_bios_scratch_regs(rdev);
> >> >>
> >> >> - radeon_fbdev_set_suspend(rdev, 0);
> >> >> - console_unlock();
> >> >> -
> >> >> + if (fbcon) {
> >> >> + radeon_fbdev_set_suspend(rdev, 0);
> >> >> + console_unlock();
> >> >> + }
> >> >> +
> >> >> /* init dig PHYs, disp eng pll */
> >> >> if (rdev->is_atom_bios) {
> >> >> radeon_atom_encoder_init(rdev);
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
> >> >> b/drivers/gpu/drm/radeon/radeon_display.c
> >> >> index 0d1aa05..bc37e33 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_display.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> >> >> @@ -30,6 +30,7 @@
> >> >> #include "atom.h"
> >> >> #include <asm/div64.h>
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> #include <drm/drm_crtc_helper.h>
> >> >> #include <drm/drm_edid.h>
> >> >>
> >> >> @@ -494,11 +495,55 @@ unlock_free:
> >> >> return r;
> >> >> }
> >> >>
> >> >> +static int
> >> >> +radeon_crtc_set_config(struct drm_mode_set *set)
> >> >> +{
> >> >> + struct drm_device *dev;
> >> >> + struct radeon_device *rdev;
> >> >> + struct drm_crtc *crtc;
> >> >> + bool active = false;
> >> >> + int ret;
> >> >> +
> >> >> + if (!set || !set->crtc)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + dev = set->crtc->dev;
> >> >> +
> >> >> + ret = pm_runtime_get_sync(dev->dev);
> >> >> + if (ret < 0)
> >> >> + return ret;
> >> >> +
> >> >> + ret = drm_crtc_helper_set_config(set);
> >> >> +
> >> >> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> >> >> + if (crtc->enabled)
> >> >> + active = true;
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> +
> >> >> + rdev = dev->dev_private;
> >> >> + /* if we have active crtcs and we don't have a power ref,
> >> >> + take the current one */
> >> >> + if (active && !rdev->have_disp_power_ref) {
> >> >> + rdev->have_disp_power_ref = true;
> >> >> + return ret;
> >> >> + }
> >> >> + /* if we have no active crtcs, then drop the power ref
> >> >> + we got before */
> >> >> + if (!active && rdev->have_disp_power_ref) {
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + rdev->have_disp_power_ref = false;
> >> >> + }
> >> >> +
> >> >> + /* drop the power reference we got coming in here */
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + return ret;
> >> >> +}
> >> >> static const struct drm_crtc_funcs radeon_crtc_funcs = {
> >> >> .cursor_set = radeon_crtc_cursor_set,
> >> >> .cursor_move = radeon_crtc_cursor_move,
> >> >> .gamma_set = radeon_crtc_gamma_set,
> >> >> - .set_config = drm_crtc_helper_set_config,
> >> >> + .set_config = radeon_crtc_set_config,
> >> >> .destroy = radeon_crtc_destroy,
> >> >> .page_flip = radeon_crtc_page_flip,
> >> >> };
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> b/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> index 788bfb0..427c64f 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> @@ -36,8 +36,9 @@
> >> >> #include <drm/drm_pciids.h>
> >> >> #include <linux/console.h>
> >> >> #include <linux/module.h>
> >> >> -
> >> >> -
> >> >> +#include <linux/pm_runtime.h>
> >> >> +#include <linux/vga_switcheroo.h>
> >> >> +#include "drm_crtc_helper.h"
> >> >> /*
> >> >> * KMS wrapper.
> >> >> * - 2.0.0 - initial interface
> >> >> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device
> >> >> *dev,
> >> >> struct drm_file *file_priv);
> >> >> void radeon_driver_preclose_kms(struct drm_device *dev,
> >> >> struct drm_file *file_priv);
> >> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> >> -int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> >> >> fbcon);
> >> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> >> >> fbcon);
> >> >> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
> >> >> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
> >> >> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
> >> >> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor
> >> >> *minor);
> >> >> #if defined(CONFIG_VGA_SWITCHEROO)
> >> >> void radeon_register_atpx_handler(void);
> >> >> void radeon_unregister_atpx_handler(void);
> >> >> +bool radeon_is_px(void);
> >> >> #else
> >> >> static inline void radeon_register_atpx_handler(void) {}
> >> >> static inline void radeon_unregister_atpx_handler(void) {}
> >> >> +static inline bool radeon_is_px(void) { return false; }
> >> >> #endif
> >> >>
> >> >> int radeon_no_wb;
> >> >> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
> >> >> int radeon_fastfb = 0;
> >> >> int radeon_dpm = -1;
> >> >> int radeon_aspm = -1;
> >> >> +int radeon_runtime_pm = -1;
> >> >>
> >> >> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch
> >> >> registers");
> >> >> module_param_named(no_wb, radeon_no_wb, int, 0444);
> >> >> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
> >> >> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
> >> >> auto)");
> >> >> module_param_named(aspm, radeon_aspm, int, 0444);
> >> >>
> >> >> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 =
> disable,
> >> >> -1
> >> >> = PX only default)");
> >> >> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
> >> >> +
> >> >> static struct pci_device_id pciidlist[] = {
> >> >> radeon_PCI_IDS
> >> >> };
> >> >> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
> >> >> return 0;
> >> >> }
> >> >>
> >> >> +
> >> >> static const struct file_operations radeon_driver_old_fops = {
> >> >> .owner = THIS_MODULE,
> >> >> .open = drm_open,
> >> >> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device
> >> >> *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_suspend_kms(drm_dev, 1);
> >> >> + return radeon_suspend_kms(drm_dev, true, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_resume(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_resume_kms(drm_dev, 1);
> >> >> + return radeon_resume_kms(drm_dev, true, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_freeze(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_suspend_kms(drm_dev, 0);
> >> >> + return radeon_suspend_kms(drm_dev, false, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_thaw(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_resume_kms(drm_dev, 0);
> >> >> + return radeon_resume_kms(drm_dev, false, true);
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_suspend(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + int ret;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> + drm_kms_helper_poll_disable(drm_dev);
> >> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
> >> >> +
> >> >> + ret = radeon_suspend_kms(drm_dev, false, false);
> >> >> + pci_save_state(pdev);
> >> >> + pci_disable_device(pdev);
> >> >> + pci_set_power_state(pdev, PCI_D3cold);
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
> >> >> +
> >> >> + return 0;
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_resume(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + int ret;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> +
> >> >> + pci_set_power_state(pdev, PCI_D0);
> >> >> + pci_restore_state(pdev);
> >> >> + ret = pci_enable_device(pdev);
> >> >> + if (ret)
> >> >> + return ret;
> >> >> + pci_set_master(pdev);
> >> >> +
> >> >> + ret = radeon_resume_kms(drm_dev, false, false);
> >> >> + drm_kms_helper_poll_enable(drm_dev);
> >> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
> >> >> + return 0;
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_idle(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + struct drm_crtc *crtc;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EBUSY;
> >> >> +
> >> >> + /* are we PX enabled? */
> >> >> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
> >> >> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
> >> >> + return -EBUSY;
> >> >> + }
> >> >> +
> >> >> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list,
> >> >> head) {
> >> >> + if (crtc->enabled) {
> >> >> + DRM_DEBUG_DRIVER("failing to power off - crtc
> >> >> active\n");
> >> >> + return -EBUSY;
> >> >> + }
> >> >> + }
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev);
> >> >> + pm_runtime_autosuspend(dev);
> >> >> + /* we don't want the main rpm_idle to call suspend - we want
> to
> >> >> autosuspend */
> >> >> + return 1;
> >> >> +}
> >> >> +
> >> >> +long radeon_drm_ioctl(struct file *filp,
> >> >> + unsigned int cmd, unsigned long arg)
> >> >> +{
> >> >> + struct drm_file *file_priv = filp->private_data;
> >> >> + struct drm_device *dev;
> >> >> + long ret;
> >> >> + dev = file_priv->minor->dev;
> >> >> + ret = pm_runtime_get_sync(dev->dev);
> >> >> + if (ret < 0)
> >> >> + return ret;
> >> >> +
> >> >> + ret = drm_ioctl(filp, cmd, arg);
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + return ret;
> >> >> }
> >> >>
> >> >> static const struct dev_pm_ops radeon_pm_ops = {
> >> >> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops =
> {
> >> >> .thaw = radeon_pmops_thaw,
> >> >> .poweroff = radeon_pmops_freeze,
> >> >> .restore = radeon_pmops_resume,
> >> >> + .runtime_suspend = radeon_pmops_runtime_suspend,
> >> >> + .runtime_resume = radeon_pmops_runtime_resume,
> >> >> + .runtime_idle = radeon_pmops_runtime_idle,
> >> >> };
> >> >>
> >> >> static const struct file_operations radeon_driver_kms_fops = {
> >> >> .owner = THIS_MODULE,
> >> >> .open = drm_open,
> >> >> .release = drm_release,
> >> >> - .unlocked_ioctl = drm_ioctl,
> >> >> + .unlocked_ioctl = radeon_drm_ioctl,
> >> >> .mmap = radeon_mmap,
> >> >> .poll = drm_poll,
> >> >> .read = drm_read,
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> b/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> index b369d42..543dcfa 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> @@ -113,6 +113,9 @@
> >> >> #define DRIVER_MINOR 33
> >> >> #define DRIVER_PATCHLEVEL 0
> >> >>
> >> >> +long radeon_drm_ioctl(struct file *filp,
> >> >> + unsigned int cmd, unsigned long arg);
> >> >> +
> >> >> /* The rest of the file is DEPRECATED! */
> >> >> #ifdef CONFIG_DRM_RADEON_UMS
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> index c180df8..bdb0f93 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
> >> >> unsigned int cmd, unsigned long
> >> >> if (nr < DRM_COMMAND_BASE)
> >> >> return drm_compat_ioctl(filp, cmd, arg);
> >> >>
> >> >> - ret = drm_ioctl(filp, cmd, arg);
> >> >> + ret = radeon_drm_ioctl(filp, cmd, arg);
> >> >>
> >> >> return ret;
> >> >> }
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> index cc9e848..ec6240b 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> @@ -32,6 +32,8 @@
> >> >> #include "radeon.h"
> >> >> #include "atom.h"
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> +
> >> >> #define RADEON_WAIT_IDLE_TIMEOUT 200
> >> >>
> >> >> /**
> >> >> @@ -47,8 +49,12 @@ irqreturn_t
> >> >> radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
> >> >> {
> >> >> struct drm_device *dev = (struct drm_device *) arg;
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> + irqreturn_t ret;
> >> >>
> >> >> - return radeon_irq_process(rdev);
> >> >> + ret = radeon_irq_process(rdev);
> >> >> + if (ret == IRQ_HANDLED)
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + return ret;
> >> >> }
> >> >>
> >> >> /*
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> b/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> index 61580dd..bffff51 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> @@ -32,7 +32,7 @@
> >> >>
> >> >> #include <linux/vga_switcheroo.h>
> >> >> #include <linux/slab.h>
> >> >> -
> >> >> +#include <linux/pm_runtime.h>
> >> >> /**
> >> >> * radeon_driver_unload_kms - Main unload function for KMS.
> >> >> *
> >> >> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device
> *dev)
> >> >>
> >> >> if (rdev == NULL)
> >> >> return 0;
> >> >> +
> >> >> if (rdev->rmmio == NULL)
> >> >> goto done_free;
> >> >> +
> >> >> + pm_runtime_get_sync(dev->dev);
> >> >> +
> >> >> radeon_acpi_fini(rdev);
> >> >> +
> >> >> radeon_modeset_fini(rdev);
> >> >> radeon_device_fini(rdev);
> >> >>
> >> >> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device
> *dev,
> >> >> unsigned long flags)
> >> >> "Error during ACPI methods call\n");
> >> >> }
> >> >>
> >> >> + if (radeon_runtime_pm != 0) {
> >> >> + pm_runtime_use_autosuspend(dev->dev);
> >> >> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
> >> >> + pm_runtime_set_active(dev->dev);
> >> >> + pm_runtime_allow(dev->dev);
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + }
> >> >> +
> >> >> out:
> >> >> if (r)
> >> >> radeon_driver_unload_kms(dev);
> >> >> +
> >> >> +
> >> >> return r;
> >> >> }
> >> >>
> >> >> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct
> drm_device
> >> >> *dev)
> >> >> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
> >> >> *file_priv)
> >> >> {
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> + int r;
> >> >>
> >> >> file_priv->driver_priv = NULL;
> >> >>
> >> >> + r = pm_runtime_get_sync(dev->dev);
> >> >> + if (r < 0)
> >> >> + return r;
> >> >> +
> >> >> /* new gpu have virtual address space support */
> >> >> if (rdev->family >= CHIP_CAYMAN) {
> >> >> struct radeon_fpriv *fpriv;
> >> >> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device
> *dev,
> >> >> struct drm_file *file_priv)
> >> >>
> >> >> file_priv->driver_priv = fpriv;
> >> >> }
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> return 0;
> >> >> }
> >> >>
> >> >> --
> >> >> 1.8.3.1
> >> >>
> >> >> _______________________________________________
> >> >> dri-devel mailing list
> >> >> dri-devel@lists.freedesktop.org
> >> >> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> >> >
> >> >
>
[-- Attachment #1.2: Type: text/html, Size: 51902 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 22:12 ` Alex Deucher
2013-09-20 22:26 ` Mike Lothian
@ 2013-09-20 22:30 ` Mike Lothian
2013-09-20 22:53 ` Dave Airlie
1 sibling, 1 reply; 10+ messages in thread
From: Mike Lothian @ 2013-09-20 22:30 UTC (permalink / raw)
To: Alex Deucher; +Cc: Alex Deucher, Dave Airlie, Maling list - DRI developers
[-- Attachment #1.1: Type: text/plain, Size: 36624 bytes --]
It's probably easier if I just show you this:
http://pastebin.com/xpBJkZDw
Is that expected behaviour? I'm used to seeing something more definite when
echoing OFF into /sys/kernel/debug/vgaswitcheroo/switch
I've just confirmed with powertop that the laptop is using 26 watts when
idle and 47 watts with DRI_PRIME=1 glxgears running
On 20 September 2013 22:12, Alex Deucher <alexdeucher@gmail.com> wrote:
> On Fri, Sep 20, 2013 at 6:10 PM, Mike Lothian <mike@fireburn.co.uk> wrote:
> > Sorry that was a typo on my part. I'm using radeon.runpm=1
> >
> > I can see audio is switched off
> >
> > [drm] Disabling audio 0 support
> >
> > When I use DRI_PRIME=1 I see the DRM initialisation for my card each
> time I
> > fire up an application or run glxinfo
>
> Yup. that's the card resuming.
>
> >
> > There isn't any explicit messages that the card is on of off however
> >
> > How can I tell if I have a powerxpress card?
>
> If you have a laptop with an integrated GPU and a discrete GPU. You
> should see a message about atpx.
>
> Alex
>
> >
> > Thanks again
> >
> > Mike
> >
> > On 20 Sep 2013 22:05, "Alex Deucher" <alexdeucher@gmail.com> wrote:
> >>
> >> On Fri, Sep 20, 2013 at 4:25 PM, Mike Lothian <mike@fireburn.co.uk>
> wrote:
> >> > Hi
> >> >
> >> > Is there an easy way to check this is on?
> >>
> >> It's on by default if your system is a powerxpress system (hybrid
> laptop).
> >>
> >> >
> >> > I have radeon.dynpm=1 in grub but usually when I use switcheroo I see
> >> > messages saying the card if now off at the moment I can old see
> messages
> >> > saying when the card gets powered up
> >> >
> >>
> >> The option is radeon.runpm for this. Note that only powerxpress
> >> systems are supported. There is no support for powering down
> >> arbitrary cards yet.
> >>
> >> > Is it possible to have the on and off messages appearing?
> >>
> >> On a supported system, you will see suspend and resume messages when
> >> when the card is powered down/up.
> >>
> >> Alex
> >>
> >> >
> >> > Cheers
> >> >
> >> > Mike
> >> >
> >> >
> >> > On 20 September 2013 18:18, Alex Deucher <alexdeucher@gmail.com>
> wrote:
> >> >>
> >> >> From: Dave Airlie <airlied@redhat.com>
> >> >>
> >> >> This hooks radeon up to the runtime PM system to enable
> >> >> dynamic power management for secondary GPUs in switchable
> >> >> and powerxpress laptops.
> >> >>
> >> >> v2: agd5f: clean up, add module parameter
> >> >>
> >> >> Signed-off-by: Dave Airlie <airlied@redhat.com>
> >> >> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
> >> >> ---
> >> >> drivers/gpu/drm/radeon/radeon.h | 8 +-
> >> >> drivers/gpu/drm/radeon/radeon_atpx_handler.c | 4 +
> >> >> drivers/gpu/drm/radeon/radeon_connectors.c | 63 ++++++++++++--
> >> >> drivers/gpu/drm/radeon/radeon_device.c | 52 +++++++++---
> >> >> drivers/gpu/drm/radeon/radeon_display.c | 47 ++++++++++-
> >> >> drivers/gpu/drm/radeon/radeon_drv.c | 122
> >> >> +++++++++++++++++++++++++--
> >> >> drivers/gpu/drm/radeon/radeon_drv.h | 3 +
> >> >> drivers/gpu/drm/radeon/radeon_ioc32.c | 2 +-
> >> >> drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +-
> >> >> drivers/gpu/drm/radeon/radeon_kms.c | 26 +++++-
> >> >> 10 files changed, 299 insertions(+), 36 deletions(-)
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon.h
> >> >> b/drivers/gpu/drm/radeon/radeon.h
> >> >> index 986100a..ad54525 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon.h
> >> >> +++ b/drivers/gpu/drm/radeon/radeon.h
> >> >> @@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
> >> >> extern int radeon_fastfb;
> >> >> extern int radeon_dpm;
> >> >> extern int radeon_aspm;
> >> >> +extern int radeon_runtime_pm;
> >> >>
> >> >> /*
> >> >> * Copy from radeon_drv.h so we don't have to include both and have
> >> >> conflicting
> >> >> @@ -2212,6 +2213,9 @@ struct radeon_device {
> >> >> /* clock, powergating flags */
> >> >> u32 cg_flags;
> >> >> u32 pg_flags;
> >> >> +
> >> >> + struct dev_pm_domain vga_pm_domain;
> >> >> + bool have_disp_power_ref;
> >> >> };
> >> >>
> >> >> int radeon_device_init(struct radeon_device *rdev,
> >> >> @@ -2673,8 +2677,8 @@ extern void
> >> >> radeon_ttm_placement_from_domain(struct
> >> >> radeon_bo *rbo, u32 domain);
> >> >> extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object
> *bo);
> >> >> extern void radeon_vram_location(struct radeon_device *rdev, struct
> >> >> radeon_mc *mc, u64 base);
> >> >> extern void radeon_gtt_location(struct radeon_device *rdev, struct
> >> >> radeon_mc *mc);
> >> >> -extern int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> >> -extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> >> +extern int radeon_resume_kms(struct drm_device *dev, bool resume,
> bool
> >> >> fbcon);
> >> >> +extern int radeon_suspend_kms(struct drm_device *dev, bool suspend,
> >> >> bool
> >> >> fbcon);
> >> >> extern void radeon_ttm_set_active_vram_size(struct radeon_device
> >> >> *rdev,
> >> >> u64 size);
> >> >> extern void radeon_program_register_sequence(struct radeon_device
> >> >> *rdev,
> >> >> const u32 *registers,
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> index d96070b..6153ec1 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
> >> >> @@ -59,6 +59,10 @@ struct atpx_mux {
> >> >> u16 mux;
> >> >> } __packed;
> >> >>
> >> >> +bool radeon_is_px(void) {
> >> >> + return radeon_atpx_priv.atpx_detected;
> >> >> +}
> >> >> +
> >> >> /**
> >> >> * radeon_atpx_call - call an ATPX method
> >> >> *
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> index 79159b5..5855b5b 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> >> >> @@ -31,6 +31,8 @@
> >> >> #include "radeon.h"
> >> >> #include "atom.h"
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> +
> >> >> extern void
> >> >> radeon_combios_connected_scratch_regs(struct drm_connector
> *connector,
> >> >> struct drm_encoder *encoder,
> >> >> @@ -626,6 +628,11 @@ radeon_lvds_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> struct radeon_connector *radeon_connector =
> >> >> to_radeon_connector(connector);
> >> >> struct drm_encoder *encoder =
> >> >> radeon_best_single_encoder(connector);
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >> +
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >>
> >> >> if (encoder) {
> >> >> struct radeon_encoder *radeon_encoder =
> >> >> to_radeon_encoder(encoder);
> >> >> @@ -651,6 +658,8 @@ radeon_lvds_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> /* check acpi lid status ??? */
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -750,6 +759,11 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> bool dret = false;
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >> +
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >>
> >> >> encoder = radeon_best_single_encoder(connector);
> >> >> if (!encoder)
> >> >> @@ -790,9 +804,8 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> * detected a monitor via load.
> >> >> */
> >> >> if (radeon_connector->detected_by_load)
> >> >> - return connector->status;
> >> >> - else
> >> >> - return ret;
> >> >> + ret = connector->status;
> >> >> + goto out;
> >> >> }
> >> >>
> >> >> if (radeon_connector->dac_load_detect && encoder) {
> >> >> @@ -817,6 +830,11 @@ radeon_vga_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> }
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +
> >> >> +out:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -873,10 +891,15 @@ radeon_tv_detect(struct drm_connector
> *connector,
> >> >> bool force)
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> struct radeon_connector *radeon_connector =
> >> >> to_radeon_connector(connector);
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> + int r;
> >> >>
> >> >> if (!radeon_connector->dac_load_detect)
> >> >> return ret;
> >> >>
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> encoder = radeon_best_single_encoder(connector);
> >> >> if (!encoder)
> >> >> ret = connector_status_disconnected;
> >> >> @@ -887,6 +910,8 @@ radeon_tv_detect(struct drm_connector *connector,
> >> >> bool
> >> >> force)
> >> >> if (ret == connector_status_connected)
> >> >> ret =
> >> >> radeon_connector_analog_encoder_conflict_solve(connector, encoder,
> ret,
> >> >> false);
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -954,12 +979,18 @@ radeon_dvi_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> struct drm_encoder *encoder = NULL;
> >> >> struct drm_encoder_helper_funcs *encoder_funcs;
> >> >> struct drm_mode_object *obj;
> >> >> - int i;
> >> >> + int i, r;
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> bool dret = false, broken_edid = false;
> >> >>
> >> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> >> - return connector->status;
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> >> + ret = connector->status;
> >> >> + goto exit;
> >> >> + }
> >> >>
> >> >> if (radeon_connector->ddc_bus)
> >> >> dret = radeon_ddc_probe(radeon_connector, false);
> >> >> @@ -1110,6 +1141,11 @@ out:
> >> >>
> >> >> /* updated in get modes as well since we need to know if it's
> >> >> analog or digital */
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +
> >> >> +exit:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> @@ -1377,9 +1413,16 @@ radeon_dp_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> enum drm_connector_status ret =
> connector_status_disconnected;
> >> >> struct radeon_connector_atom_dig *radeon_dig_connector =
> >> >> radeon_connector->con_priv;
> >> >> struct drm_encoder *encoder =
> >> >> radeon_best_single_encoder(connector);
> >> >> + int r;
> >> >>
> >> >> - if (!force && radeon_check_hpd_status_unchanged(connector))
> >> >> - return connector->status;
> >> >> + r = pm_runtime_get_sync(connector->dev->dev);
> >> >> + if (r < 0)
> >> >> + return connector_status_disconnected;
> >> >> +
> >> >> + if (!force && radeon_check_hpd_status_unchanged(connector)) {
> >> >> + ret = connector->status;
> >> >> + goto out;
> >> >> + }
> >> >>
> >> >> if (radeon_connector->edid) {
> >> >> kfree(radeon_connector->edid);
> >> >> @@ -1443,6 +1486,10 @@ radeon_dp_detect(struct drm_connector
> >> >> *connector,
> >> >> bool force)
> >> >> }
> >> >>
> >> >> radeon_connector_update_scratch_regs(connector, ret);
> >> >> +out:
> >> >> + pm_runtime_mark_last_busy(connector->dev->dev);
> >> >> + pm_runtime_put_autosuspend(connector->dev->dev);
> >> >> +
> >> >> return ret;
> >> >> }
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_device.c
> >> >> b/drivers/gpu/drm/radeon/radeon_device.c
> >> >> index 37cfcee..b9b9dfd 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_device.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_device.c
> >> >> @@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
> >> >> "LAST",
> >> >> };
> >> >>
> >> >> +#if defined(CONFIG_VGA_SWITCHEROO)
> >> >> +bool radeon_is_px(void);
> >> >> +#else
> >> >> +static inline bool radeon_is_px(void) { return false; }
> >> >> +#endif
> >> >> +
> >> >> /**
> >> >> * radeon_program_register_sequence - program an array of registers.
> >> >> *
> >> >> @@ -1076,6 +1082,10 @@ static bool
> >> >> radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
> >> >> static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum
> >> >> vga_switcheroo_state state)
> >> >> {
> >> >> struct drm_device *dev = pci_get_drvdata(pdev);
> >> >> +
> >> >> + if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
> >> >> + return;
> >> >> +
> >> >> if (state == VGA_SWITCHEROO_ON) {
> >> >> unsigned d3_delay = dev->pdev->d3_delay;
> >> >>
> >> >> @@ -1086,7 +1096,7 @@ static void radeon_switcheroo_set_state(struct
> >> >> pci_dev *pdev, enum vga_switchero
> >> >> if (d3_delay < 20 &&
> >> >> radeon_switcheroo_quirk_long_wakeup(pdev))
> >> >> dev->pdev->d3_delay = 20;
> >> >>
> >> >> - radeon_resume_kms(dev, 1);
> >> >> + radeon_resume_kms(dev, true, true);
> >> >>
> >> >> dev->pdev->d3_delay = d3_delay;
> >> >>
> >> >> @@ -1096,7 +1106,7 @@ static void radeon_switcheroo_set_state(struct
> >> >> pci_dev *pdev, enum vga_switchero
> >> >> printk(KERN_INFO "radeon: switched off\n");
> >> >> drm_kms_helper_poll_disable(dev);
> >> >> dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> - radeon_suspend_kms(dev, 1);
> >> >> + radeon_suspend_kms(dev, true, true);
> >> >> dev->switch_power_state = DRM_SWITCH_POWER_OFF;
> >> >> }
> >> >> }
> >> >> @@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device
> >> >> *rdev,
> >> >> {
> >> >> int r, i;
> >> >> int dma_bits;
> >> >> + bool runtime = false;
> >> >>
> >> >> rdev->shutdown = false;
> >> >> rdev->dev = &pdev->dev;
> >> >> @@ -1292,7 +1303,14 @@ int radeon_device_init(struct radeon_device
> >> >> *rdev,
> >> >> /* this will fail for cards that aren't VGA class devices,
> just
> >> >> * ignore it */
> >> >> vga_client_register(rdev->pdev, rdev, NULL,
> >> >> radeon_vga_set_decode);
> >> >> - vga_switcheroo_register_client(rdev->pdev,
> >> >> &radeon_switcheroo_ops,
> >> >> false);
> >> >> +
> >> >> + if (radeon_runtime_pm == 1)
> >> >> + runtime = true;
> >> >> + if ((radeon_runtime_pm == -1) && radeon_is_px())
> >> >> + runtime = true;
> >> >> + vga_switcheroo_register_client(rdev->pdev,
> >> >> &radeon_switcheroo_ops,
> >> >> runtime);
> >> >> + if (runtime)
> >> >> + vga_switcheroo_init_domain_pm_ops(rdev->dev,
> >> >> &rdev->vga_pm_domain);
> >> >>
> >> >> r = radeon_init(rdev);
> >> >> if (r)
> >> >> @@ -1373,7 +1391,7 @@ void radeon_device_fini(struct radeon_device
> >> >> *rdev)
> >> >> * Returns 0 for success or an error on failure.
> >> >> * Called at driver suspend.
> >> >> */
> >> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend)
> >> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> >> >> fbcon)
> >> >> {
> >> >> struct radeon_device *rdev;
> >> >> struct drm_crtc *crtc;
> >> >> @@ -1448,9 +1466,12 @@ int radeon_suspend_kms(struct drm_device *dev,
> >> >> bool
> >> >> suspend)
> >> >> pci_disable_device(dev->pdev);
> >> >> pci_set_power_state(dev->pdev, PCI_D3hot);
> >> >> }
> >> >> - console_lock();
> >> >> - radeon_fbdev_set_suspend(rdev, 1);
> >> >> - console_unlock();
> >> >> +
> >> >> + if (fbcon) {
> >> >> + console_lock();
> >> >> + radeon_fbdev_set_suspend(rdev, 1);
> >> >> + console_unlock();
> >> >> + }
> >> >> return 0;
> >> >> }
> >> >>
> >> >> @@ -1463,7 +1484,7 @@ int radeon_suspend_kms(struct drm_device *dev,
> >> >> bool
> >> >> suspend)
> >> >> * Returns 0 for success or an error on failure.
> >> >> * Called at driver resume.
> >> >> */
> >> >> -int radeon_resume_kms(struct drm_device *dev, bool resume)
> >> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> fbcon)
> >> >> {
> >> >> struct drm_connector *connector;
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> @@ -1472,12 +1493,15 @@ int radeon_resume_kms(struct drm_device *dev,
> >> >> bool
> >> >> resume)
> >> >> if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
> >> >> return 0;
> >> >>
> >> >> - console_lock();
> >> >> + if (fbcon) {
> >> >> + console_lock();
> >> >> + }
> >> >> if (resume) {
> >> >> pci_set_power_state(dev->pdev, PCI_D0);
> >> >> pci_restore_state(dev->pdev);
> >> >> if (pci_enable_device(dev->pdev)) {
> >> >> - console_unlock();
> >> >> + if (fbcon)
> >> >> + console_unlock();
> >> >> return -1;
> >> >> }
> >> >> }
> >> >> @@ -1492,9 +1516,11 @@ int radeon_resume_kms(struct drm_device *dev,
> >> >> bool
> >> >> resume)
> >> >> radeon_pm_resume(rdev);
> >> >> radeon_restore_bios_scratch_regs(rdev);
> >> >>
> >> >> - radeon_fbdev_set_suspend(rdev, 0);
> >> >> - console_unlock();
> >> >> -
> >> >> + if (fbcon) {
> >> >> + radeon_fbdev_set_suspend(rdev, 0);
> >> >> + console_unlock();
> >> >> + }
> >> >> +
> >> >> /* init dig PHYs, disp eng pll */
> >> >> if (rdev->is_atom_bios) {
> >> >> radeon_atom_encoder_init(rdev);
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_display.c
> >> >> b/drivers/gpu/drm/radeon/radeon_display.c
> >> >> index 0d1aa05..bc37e33 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_display.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> >> >> @@ -30,6 +30,7 @@
> >> >> #include "atom.h"
> >> >> #include <asm/div64.h>
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> #include <drm/drm_crtc_helper.h>
> >> >> #include <drm/drm_edid.h>
> >> >>
> >> >> @@ -494,11 +495,55 @@ unlock_free:
> >> >> return r;
> >> >> }
> >> >>
> >> >> +static int
> >> >> +radeon_crtc_set_config(struct drm_mode_set *set)
> >> >> +{
> >> >> + struct drm_device *dev;
> >> >> + struct radeon_device *rdev;
> >> >> + struct drm_crtc *crtc;
> >> >> + bool active = false;
> >> >> + int ret;
> >> >> +
> >> >> + if (!set || !set->crtc)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + dev = set->crtc->dev;
> >> >> +
> >> >> + ret = pm_runtime_get_sync(dev->dev);
> >> >> + if (ret < 0)
> >> >> + return ret;
> >> >> +
> >> >> + ret = drm_crtc_helper_set_config(set);
> >> >> +
> >> >> + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> >> >> + if (crtc->enabled)
> >> >> + active = true;
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> +
> >> >> + rdev = dev->dev_private;
> >> >> + /* if we have active crtcs and we don't have a power ref,
> >> >> + take the current one */
> >> >> + if (active && !rdev->have_disp_power_ref) {
> >> >> + rdev->have_disp_power_ref = true;
> >> >> + return ret;
> >> >> + }
> >> >> + /* if we have no active crtcs, then drop the power ref
> >> >> + we got before */
> >> >> + if (!active && rdev->have_disp_power_ref) {
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + rdev->have_disp_power_ref = false;
> >> >> + }
> >> >> +
> >> >> + /* drop the power reference we got coming in here */
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + return ret;
> >> >> +}
> >> >> static const struct drm_crtc_funcs radeon_crtc_funcs = {
> >> >> .cursor_set = radeon_crtc_cursor_set,
> >> >> .cursor_move = radeon_crtc_cursor_move,
> >> >> .gamma_set = radeon_crtc_gamma_set,
> >> >> - .set_config = drm_crtc_helper_set_config,
> >> >> + .set_config = radeon_crtc_set_config,
> >> >> .destroy = radeon_crtc_destroy,
> >> >> .page_flip = radeon_crtc_page_flip,
> >> >> };
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> b/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> index 788bfb0..427c64f 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> >> >> @@ -36,8 +36,9 @@
> >> >> #include <drm/drm_pciids.h>
> >> >> #include <linux/console.h>
> >> >> #include <linux/module.h>
> >> >> -
> >> >> -
> >> >> +#include <linux/pm_runtime.h>
> >> >> +#include <linux/vga_switcheroo.h>
> >> >> +#include "drm_crtc_helper.h"
> >> >> /*
> >> >> * KMS wrapper.
> >> >> * - 2.0.0 - initial interface
> >> >> @@ -87,8 +88,8 @@ void radeon_driver_postclose_kms(struct drm_device
> >> >> *dev,
> >> >> struct drm_file *file_priv);
> >> >> void radeon_driver_preclose_kms(struct drm_device *dev,
> >> >> struct drm_file *file_priv);
> >> >> -int radeon_suspend_kms(struct drm_device *dev, bool suspend);
> >> >> -int radeon_resume_kms(struct drm_device *dev, bool resume);
> >> >> +int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool
> >> >> fbcon);
> >> >> +int radeon_resume_kms(struct drm_device *dev, bool resume, bool
> >> >> fbcon);
> >> >> u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
> >> >> int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
> >> >> void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
> >> >> @@ -137,9 +138,11 @@ void radeon_debugfs_cleanup(struct drm_minor
> >> >> *minor);
> >> >> #if defined(CONFIG_VGA_SWITCHEROO)
> >> >> void radeon_register_atpx_handler(void);
> >> >> void radeon_unregister_atpx_handler(void);
> >> >> +bool radeon_is_px(void);
> >> >> #else
> >> >> static inline void radeon_register_atpx_handler(void) {}
> >> >> static inline void radeon_unregister_atpx_handler(void) {}
> >> >> +static inline bool radeon_is_px(void) { return false; }
> >> >> #endif
> >> >>
> >> >> int radeon_no_wb;
> >> >> @@ -162,6 +165,7 @@ int radeon_lockup_timeout = 10000;
> >> >> int radeon_fastfb = 0;
> >> >> int radeon_dpm = -1;
> >> >> int radeon_aspm = -1;
> >> >> +int radeon_runtime_pm = -1;
> >> >>
> >> >> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch
> >> >> registers");
> >> >> module_param_named(no_wb, radeon_no_wb, int, 0444);
> >> >> @@ -223,6 +227,9 @@ module_param_named(dpm, radeon_dpm, int, 0444);
> >> >> MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 =
> >> >> auto)");
> >> >> module_param_named(aspm, radeon_aspm, int, 0444);
> >> >>
> >> >> +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 =
> disable,
> >> >> -1
> >> >> = PX only default)");
> >> >> +module_param_named(runpm, radeon_runtime_pm, int, 0444);
> >> >> +
> >> >> static struct pci_device_id pciidlist[] = {
> >> >> radeon_PCI_IDS
> >> >> };
> >> >> @@ -259,6 +266,7 @@ static int radeon_resume(struct drm_device *dev)
> >> >> return 0;
> >> >> }
> >> >>
> >> >> +
> >> >> static const struct file_operations radeon_driver_old_fops = {
> >> >> .owner = THIS_MODULE,
> >> >> .open = drm_open,
> >> >> @@ -357,28 +365,121 @@ static int radeon_pmops_suspend(struct device
> >> >> *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_suspend_kms(drm_dev, 1);
> >> >> + return radeon_suspend_kms(drm_dev, true, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_resume(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_resume_kms(drm_dev, 1);
> >> >> + return radeon_resume_kms(drm_dev, true, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_freeze(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_suspend_kms(drm_dev, 0);
> >> >> + return radeon_suspend_kms(drm_dev, false, true);
> >> >> }
> >> >>
> >> >> static int radeon_pmops_thaw(struct device *dev)
> >> >> {
> >> >> struct pci_dev *pdev = to_pci_dev(dev);
> >> >> struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> - return radeon_resume_kms(drm_dev, 0);
> >> >> + return radeon_resume_kms(drm_dev, false, true);
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_suspend(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + int ret;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> + drm_kms_helper_poll_disable(drm_dev);
> >> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
> >> >> +
> >> >> + ret = radeon_suspend_kms(drm_dev, false, false);
> >> >> + pci_save_state(pdev);
> >> >> + pci_disable_device(pdev);
> >> >> + pci_set_power_state(pdev, PCI_D3cold);
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
> >> >> +
> >> >> + return 0;
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_resume(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + int ret;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EINVAL;
> >> >> +
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
> >> >> +
> >> >> + pci_set_power_state(pdev, PCI_D0);
> >> >> + pci_restore_state(pdev);
> >> >> + ret = pci_enable_device(pdev);
> >> >> + if (ret)
> >> >> + return ret;
> >> >> + pci_set_master(pdev);
> >> >> +
> >> >> + ret = radeon_resume_kms(drm_dev, false, false);
> >> >> + drm_kms_helper_poll_enable(drm_dev);
> >> >> + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
> >> >> + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
> >> >> + return 0;
> >> >> +}
> >> >> +
> >> >> +static int radeon_pmops_runtime_idle(struct device *dev)
> >> >> +{
> >> >> + struct pci_dev *pdev = to_pci_dev(dev);
> >> >> + struct drm_device *drm_dev = pci_get_drvdata(pdev);
> >> >> + struct drm_crtc *crtc;
> >> >> +
> >> >> + if (radeon_runtime_pm == 0)
> >> >> + return -EBUSY;
> >> >> +
> >> >> + /* are we PX enabled? */
> >> >> + if (radeon_runtime_pm == -1 && !radeon_is_px()) {
> >> >> + DRM_DEBUG_DRIVER("failing to power off - not px\n");
> >> >> + return -EBUSY;
> >> >> + }
> >> >> +
> >> >> + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list,
> >> >> head) {
> >> >> + if (crtc->enabled) {
> >> >> + DRM_DEBUG_DRIVER("failing to power off - crtc
> >> >> active\n");
> >> >> + return -EBUSY;
> >> >> + }
> >> >> + }
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev);
> >> >> + pm_runtime_autosuspend(dev);
> >> >> + /* we don't want the main rpm_idle to call suspend - we want
> to
> >> >> autosuspend */
> >> >> + return 1;
> >> >> +}
> >> >> +
> >> >> +long radeon_drm_ioctl(struct file *filp,
> >> >> + unsigned int cmd, unsigned long arg)
> >> >> +{
> >> >> + struct drm_file *file_priv = filp->private_data;
> >> >> + struct drm_device *dev;
> >> >> + long ret;
> >> >> + dev = file_priv->minor->dev;
> >> >> + ret = pm_runtime_get_sync(dev->dev);
> >> >> + if (ret < 0)
> >> >> + return ret;
> >> >> +
> >> >> + ret = drm_ioctl(filp, cmd, arg);
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + return ret;
> >> >> }
> >> >>
> >> >> static const struct dev_pm_ops radeon_pm_ops = {
> >> >> @@ -388,13 +489,16 @@ static const struct dev_pm_ops radeon_pm_ops =
> {
> >> >> .thaw = radeon_pmops_thaw,
> >> >> .poweroff = radeon_pmops_freeze,
> >> >> .restore = radeon_pmops_resume,
> >> >> + .runtime_suspend = radeon_pmops_runtime_suspend,
> >> >> + .runtime_resume = radeon_pmops_runtime_resume,
> >> >> + .runtime_idle = radeon_pmops_runtime_idle,
> >> >> };
> >> >>
> >> >> static const struct file_operations radeon_driver_kms_fops = {
> >> >> .owner = THIS_MODULE,
> >> >> .open = drm_open,
> >> >> .release = drm_release,
> >> >> - .unlocked_ioctl = drm_ioctl,
> >> >> + .unlocked_ioctl = radeon_drm_ioctl,
> >> >> .mmap = radeon_mmap,
> >> >> .poll = drm_poll,
> >> >> .read = drm_read,
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> b/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> index b369d42..543dcfa 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_drv.h
> >> >> @@ -113,6 +113,9 @@
> >> >> #define DRIVER_MINOR 33
> >> >> #define DRIVER_PATCHLEVEL 0
> >> >>
> >> >> +long radeon_drm_ioctl(struct file *filp,
> >> >> + unsigned int cmd, unsigned long arg);
> >> >> +
> >> >> /* The rest of the file is DEPRECATED! */
> >> >> #ifdef CONFIG_DRM_RADEON_UMS
> >> >>
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> index c180df8..bdb0f93 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
> >> >> @@ -418,7 +418,7 @@ long radeon_kms_compat_ioctl(struct file *filp,
> >> >> unsigned int cmd, unsigned long
> >> >> if (nr < DRM_COMMAND_BASE)
> >> >> return drm_compat_ioctl(filp, cmd, arg);
> >> >>
> >> >> - ret = drm_ioctl(filp, cmd, arg);
> >> >> + ret = radeon_drm_ioctl(filp, cmd, arg);
> >> >>
> >> >> return ret;
> >> >> }
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> index cc9e848..ec6240b 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
> >> >> @@ -32,6 +32,8 @@
> >> >> #include "radeon.h"
> >> >> #include "atom.h"
> >> >>
> >> >> +#include <linux/pm_runtime.h>
> >> >> +
> >> >> #define RADEON_WAIT_IDLE_TIMEOUT 200
> >> >>
> >> >> /**
> >> >> @@ -47,8 +49,12 @@ irqreturn_t
> >> >> radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
> >> >> {
> >> >> struct drm_device *dev = (struct drm_device *) arg;
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> + irqreturn_t ret;
> >> >>
> >> >> - return radeon_irq_process(rdev);
> >> >> + ret = radeon_irq_process(rdev);
> >> >> + if (ret == IRQ_HANDLED)
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + return ret;
> >> >> }
> >> >>
> >> >> /*
> >> >> diff --git a/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> b/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> index 61580dd..bffff51 100644
> >> >> --- a/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> +++ b/drivers/gpu/drm/radeon/radeon_kms.c
> >> >> @@ -32,7 +32,7 @@
> >> >>
> >> >> #include <linux/vga_switcheroo.h>
> >> >> #include <linux/slab.h>
> >> >> -
> >> >> +#include <linux/pm_runtime.h>
> >> >> /**
> >> >> * radeon_driver_unload_kms - Main unload function for KMS.
> >> >> *
> >> >> @@ -50,9 +50,14 @@ int radeon_driver_unload_kms(struct drm_device
> *dev)
> >> >>
> >> >> if (rdev == NULL)
> >> >> return 0;
> >> >> +
> >> >> if (rdev->rmmio == NULL)
> >> >> goto done_free;
> >> >> +
> >> >> + pm_runtime_get_sync(dev->dev);
> >> >> +
> >> >> radeon_acpi_fini(rdev);
> >> >> +
> >> >> radeon_modeset_fini(rdev);
> >> >> radeon_device_fini(rdev);
> >> >>
> >> >> @@ -125,9 +130,20 @@ int radeon_driver_load_kms(struct drm_device
> *dev,
> >> >> unsigned long flags)
> >> >> "Error during ACPI methods call\n");
> >> >> }
> >> >>
> >> >> + if (radeon_runtime_pm != 0) {
> >> >> + pm_runtime_use_autosuspend(dev->dev);
> >> >> + pm_runtime_set_autosuspend_delay(dev->dev, 5000);
> >> >> + pm_runtime_set_active(dev->dev);
> >> >> + pm_runtime_allow(dev->dev);
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> + }
> >> >> +
> >> >> out:
> >> >> if (r)
> >> >> radeon_driver_unload_kms(dev);
> >> >> +
> >> >> +
> >> >> return r;
> >> >> }
> >> >>
> >> >> @@ -475,9 +491,14 @@ void radeon_driver_lastclose_kms(struct
> drm_device
> >> >> *dev)
> >> >> int radeon_driver_open_kms(struct drm_device *dev, struct drm_file
> >> >> *file_priv)
> >> >> {
> >> >> struct radeon_device *rdev = dev->dev_private;
> >> >> + int r;
> >> >>
> >> >> file_priv->driver_priv = NULL;
> >> >>
> >> >> + r = pm_runtime_get_sync(dev->dev);
> >> >> + if (r < 0)
> >> >> + return r;
> >> >> +
> >> >> /* new gpu have virtual address space support */
> >> >> if (rdev->family >= CHIP_CAYMAN) {
> >> >> struct radeon_fpriv *fpriv;
> >> >> @@ -506,6 +527,9 @@ int radeon_driver_open_kms(struct drm_device
> *dev,
> >> >> struct drm_file *file_priv)
> >> >>
> >> >> file_priv->driver_priv = fpriv;
> >> >> }
> >> >> +
> >> >> + pm_runtime_mark_last_busy(dev->dev);
> >> >> + pm_runtime_put_autosuspend(dev->dev);
> >> >> return 0;
> >> >> }
> >> >>
> >> >> --
> >> >> 1.8.3.1
> >> >>
> >> >> _______________________________________________
> >> >> dri-devel mailing list
> >> >> dri-devel@lists.freedesktop.org
> >> >> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> >> >
> >> >
>
[-- Attachment #1.2: Type: text/html, Size: 51920 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] drm/radeon: add runtime PM support (v2)
2013-09-20 22:30 ` Mike Lothian
@ 2013-09-20 22:53 ` Dave Airlie
0 siblings, 0 replies; 10+ messages in thread
From: Dave Airlie @ 2013-09-20 22:53 UTC (permalink / raw)
To: Mike Lothian; +Cc: Dave Airlie, Alex Deucher, Maling list - DRI developers
On Sat, Sep 21, 2013 at 8:30 AM, Mike Lothian <mike@fireburn.co.uk> wrote:
> It's probably easier if I just show you this:
>
> http://pastebin.com/xpBJkZDw
>
> Is that expected behaviour? I'm used to seeing something more definite when
> echoing OFF into /sys/kernel/debug/vgaswitcheroo/switch
>
> I've just confirmed with powertop that the laptop is using 26 watts when
> idle and 47 watts with DRI_PRIME=1 glxgears running
>
seems right though we should probably decrease the logging, its a lot
to spam dmesg with everytime.
for nouveau I made a log level for dynamic pm suspend/resume that
didn't log stuff, I think it should
probably be silent in dmesg and transparent to users.
Maybe with a file in sysfs or somewhere to say what the actual GPU state is,
Dave.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2013-09-20 22:53 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-20 18:18 [PATCH 0/2] Add runtime PM support to radeon Alex Deucher
2013-09-20 18:18 ` [PATCH 1/2] drm/radeon: convert to pmops Alex Deucher
2013-09-20 18:18 ` [PATCH 2/2] drm/radeon: add runtime PM support (v2) Alex Deucher
2013-09-20 20:25 ` Mike Lothian
2013-09-20 21:05 ` Alex Deucher
2013-09-20 22:10 ` Mike Lothian
2013-09-20 22:12 ` Alex Deucher
2013-09-20 22:26 ` Mike Lothian
2013-09-20 22:30 ` Mike Lothian
2013-09-20 22:53 ` Dave Airlie
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).