Skip to content

Commit

Permalink
drm/radeon: add runtime PM support (v2)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dave Airlie authored and Alex Deucher committed Nov 1, 2013
1 parent 7473e83 commit 10ebc0b
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 36 deletions.
8 changes: 6 additions & 2 deletions drivers/gpu/drm/radeon/radeon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/radeon/radeon_atpx_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
63 changes: 55 additions & 8 deletions drivers/gpu/drm/radeon/radeon_connectors.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -641,6 +643,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);
Expand All @@ -666,6 +673,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;
}

Expand Down Expand Up @@ -765,6 +774,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)
Expand Down Expand Up @@ -805,9 +819,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) {
Expand All @@ -832,6 +845,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;
}

Expand Down Expand Up @@ -888,10 +906,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;
Expand All @@ -902,6 +925,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;
}

Expand Down Expand Up @@ -969,12 +994,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);
Expand Down Expand Up @@ -1125,6 +1156,11 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)

/* 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;
}

Expand Down Expand Up @@ -1392,9 +1428,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);
Expand Down Expand Up @@ -1458,6 +1501,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;
}

Expand Down
52 changes: 39 additions & 13 deletions drivers/gpu/drm/radeon/radeon_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1382,7 +1400,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;
Expand Down Expand Up @@ -1457,9 +1475,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;
}

Expand All @@ -1472,7 +1493,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;
Expand All @@ -1481,12 +1502,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;
}
}
Expand All @@ -1501,9 +1525,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);
Expand Down
47 changes: 46 additions & 1 deletion drivers/gpu/drm/radeon/radeon_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -494,11 +495,55 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
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,
};
Expand Down
Loading

0 comments on commit 10ebc0b

Please sign in to comment.