Message ID | 1379701098-8399-3-git-send-email-alexander.deucher@amd.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 >
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 > >
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 > > > > >
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 >> > >> >
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 > >> > > >> > >
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 > >> > > >> > >
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.
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; }