Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10ebc0bc authored by Dave Airlie's avatar Dave Airlie Committed by Alex Deucher
Browse files

drm/radeon: add runtime PM support (v2)



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: default avatarDave Airlie <airlied@redhat.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 7473e830
Loading
Loading
Loading
Loading
+6 −2
Original line number Original line Diff line number Diff line
@@ -98,6 +98,7 @@ extern int radeon_lockup_timeout;
extern int radeon_fastfb;
extern int radeon_fastfb;
extern int radeon_dpm;
extern int radeon_dpm;
extern int radeon_aspm;
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
 * 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 */
	/* clock, powergating flags */
	u32 cg_flags;
	u32 cg_flags;
	u32 pg_flags;
	u32 pg_flags;

	struct dev_pm_domain vga_pm_domain;
	bool have_disp_power_ref;
};
};


int radeon_device_init(struct radeon_device *rdev,
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 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_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 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_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
extern int radeon_suspend_kms(struct drm_device *dev, bool suspend);
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_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size);
extern void radeon_program_register_sequence(struct radeon_device *rdev,
extern void radeon_program_register_sequence(struct radeon_device *rdev,
					     const u32 *registers,
					     const u32 *registers,
+4 −0
Original line number Original line Diff line number Diff line
@@ -59,6 +59,10 @@ struct atpx_mux {
	u16 mux;
	u16 mux;
} __packed;
} __packed;


bool radeon_is_px(void) {
	return radeon_atpx_priv.atpx_detected;
}

/**
/**
 * radeon_atpx_call - call an ATPX method
 * radeon_atpx_call - call an ATPX method
 *
 *
+55 −8
Original line number Original line Diff line number Diff line
@@ -31,6 +31,8 @@
#include "radeon.h"
#include "radeon.h"
#include "atom.h"
#include "atom.h"


#include <linux/pm_runtime.h>

extern void
extern void
radeon_combios_connected_scratch_regs(struct drm_connector *connector,
radeon_combios_connected_scratch_regs(struct drm_connector *connector,
				      struct drm_encoder *encoder,
				      struct drm_encoder *encoder,
@@ -641,6 +643,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
	struct drm_encoder *encoder = radeon_best_single_encoder(connector);
	struct drm_encoder *encoder = radeon_best_single_encoder(connector);
	enum drm_connector_status ret = connector_status_disconnected;
	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) {
	if (encoder) {
		struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
		struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -666,6 +673,8 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
	/* check acpi lid status ??? */
	/* check acpi lid status ??? */


	radeon_connector_update_scratch_regs(connector, ret);
	radeon_connector_update_scratch_regs(connector, ret);
	pm_runtime_mark_last_busy(connector->dev->dev);
	pm_runtime_put_autosuspend(connector->dev->dev);
	return ret;
	return ret;
}
}


@@ -765,6 +774,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
	struct drm_encoder_helper_funcs *encoder_funcs;
	struct drm_encoder_helper_funcs *encoder_funcs;
	bool dret = false;
	bool dret = false;
	enum drm_connector_status ret = connector_status_disconnected;
	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);
	encoder = radeon_best_single_encoder(connector);
	if (!encoder)
	if (!encoder)
@@ -805,9 +819,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
			 * detected a monitor via load.
			 * detected a monitor via load.
			 */
			 */
			if (radeon_connector->detected_by_load)
			if (radeon_connector->detected_by_load)
				return connector->status;
				ret = connector->status;
			else
			goto out;
				return ret;
		}
		}


		if (radeon_connector->dac_load_detect && encoder) {
		if (radeon_connector->dac_load_detect && encoder) {
@@ -832,6 +845,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
	}
	}


	radeon_connector_update_scratch_regs(connector, ret);
	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;
	return ret;
}
}


@@ -888,10 +906,15 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
	struct drm_encoder_helper_funcs *encoder_funcs;
	struct drm_encoder_helper_funcs *encoder_funcs;
	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
	enum drm_connector_status ret = connector_status_disconnected;
	enum drm_connector_status ret = connector_status_disconnected;
	int r;


	if (!radeon_connector->dac_load_detect)
	if (!radeon_connector->dac_load_detect)
		return ret;
		return ret;


	r = pm_runtime_get_sync(connector->dev->dev);
	if (r < 0)
		return connector_status_disconnected;

	encoder = radeon_best_single_encoder(connector);
	encoder = radeon_best_single_encoder(connector);
	if (!encoder)
	if (!encoder)
		ret = connector_status_disconnected;
		ret = connector_status_disconnected;
@@ -902,6 +925,8 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
	if (ret == connector_status_connected)
	if (ret == connector_status_connected)
		ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
		ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
	radeon_connector_update_scratch_regs(connector, ret);
	radeon_connector_update_scratch_regs(connector, ret);
	pm_runtime_mark_last_busy(connector->dev->dev);
	pm_runtime_put_autosuspend(connector->dev->dev);
	return ret;
	return ret;
}
}


@@ -969,12 +994,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
	struct drm_encoder *encoder = NULL;
	struct drm_encoder *encoder = NULL;
	struct drm_encoder_helper_funcs *encoder_funcs;
	struct drm_encoder_helper_funcs *encoder_funcs;
	struct drm_mode_object *obj;
	struct drm_mode_object *obj;
	int i;
	int i, r;
	enum drm_connector_status ret = connector_status_disconnected;
	enum drm_connector_status ret = connector_status_disconnected;
	bool dret = false, broken_edid = false;
	bool dret = false, broken_edid = false;


	if (!force && radeon_check_hpd_status_unchanged(connector))
	r = pm_runtime_get_sync(connector->dev->dev);
		return connector->status;
	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)
	if (radeon_connector->ddc_bus)
		dret = radeon_ddc_probe(radeon_connector, false);
		dret = radeon_ddc_probe(radeon_connector, false);
@@ -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 */
	/* updated in get modes as well since we need to know if it's analog or digital */
	radeon_connector_update_scratch_regs(connector, ret);
	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;
	return ret;
}
}


@@ -1392,9 +1428,16 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
	enum drm_connector_status ret = connector_status_disconnected;
	enum drm_connector_status ret = connector_status_disconnected;
	struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
	struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
	struct drm_encoder *encoder = radeon_best_single_encoder(connector);
	struct drm_encoder *encoder = radeon_best_single_encoder(connector);
	int r;


	if (!force && radeon_check_hpd_status_unchanged(connector))
	r = pm_runtime_get_sync(connector->dev->dev);
		return connector->status;
	if (r < 0)
		return connector_status_disconnected;

	if (!force && radeon_check_hpd_status_unchanged(connector)) {
		ret = connector->status;
		goto out;
	}


	if (radeon_connector->edid) {
	if (radeon_connector->edid) {
		kfree(radeon_connector->edid);
		kfree(radeon_connector->edid);
@@ -1458,6 +1501,10 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
	}
	}


	radeon_connector_update_scratch_regs(connector, ret);
	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;
	return ret;
}
}


+39 −13
Original line number Original line Diff line number Diff line
@@ -101,6 +101,12 @@ static const char radeon_family_name[][16] = {
	"LAST",
	"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.
 * 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)
static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
{
{
	struct drm_device *dev = pci_get_drvdata(pdev);
	struct drm_device *dev = pci_get_drvdata(pdev);

	if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
		return;

	if (state == VGA_SWITCHEROO_ON) {
	if (state == VGA_SWITCHEROO_ON) {
		unsigned d3_delay = dev->pdev->d3_delay;
		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))
		if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev))
			dev->pdev->d3_delay = 20;
			dev->pdev->d3_delay = 20;


		radeon_resume_kms(dev, 1);
		radeon_resume_kms(dev, true, true);


		dev->pdev->d3_delay = d3_delay;
		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");
		printk(KERN_INFO "radeon: switched off\n");
		drm_kms_helper_poll_disable(dev);
		drm_kms_helper_poll_disable(dev);
		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
		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;
		dev->switch_power_state = DRM_SWITCH_POWER_OFF;
	}
	}
}
}
@@ -1146,6 +1156,7 @@ int radeon_device_init(struct radeon_device *rdev,
{
{
	int r, i;
	int r, i;
	int dma_bits;
	int dma_bits;
	bool runtime = false;


	rdev->shutdown = false;
	rdev->shutdown = false;
	rdev->dev = &pdev->dev;
	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
	/* this will fail for cards that aren't VGA class devices, just
	 * ignore it */
	 * ignore it */
	vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
	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);
	r = radeon_init(rdev);
	if (r)
	if (r)
@@ -1382,7 +1400,7 @@ void radeon_device_fini(struct radeon_device *rdev)
 * Returns 0 for success or an error on failure.
 * Returns 0 for success or an error on failure.
 * Called at driver suspend.
 * 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 radeon_device *rdev;
	struct drm_crtc *crtc;
	struct drm_crtc *crtc;
@@ -1457,9 +1475,12 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend)
		pci_disable_device(dev->pdev);
		pci_disable_device(dev->pdev);
		pci_set_power_state(dev->pdev, PCI_D3hot);
		pci_set_power_state(dev->pdev, PCI_D3hot);
	}
	}

	if (fbcon) {
		console_lock();
		console_lock();
		radeon_fbdev_set_suspend(rdev, 1);
		radeon_fbdev_set_suspend(rdev, 1);
		console_unlock();
		console_unlock();
	}
	return 0;
	return 0;
}
}


@@ -1472,7 +1493,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend)
 * Returns 0 for success or an error on failure.
 * Returns 0 for success or an error on failure.
 * Called at driver resume.
 * 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 drm_connector *connector;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_device *rdev = dev->dev_private;
@@ -1481,11 +1502,14 @@ int radeon_resume_kms(struct drm_device *dev, bool resume)
	if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
	if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
		return 0;
		return 0;


	if (fbcon) {
		console_lock();
		console_lock();
	}
	if (resume) {
	if (resume) {
		pci_set_power_state(dev->pdev, PCI_D0);
		pci_set_power_state(dev->pdev, PCI_D0);
		pci_restore_state(dev->pdev);
		pci_restore_state(dev->pdev);
		if (pci_enable_device(dev->pdev)) {
		if (pci_enable_device(dev->pdev)) {
			if (fbcon)
				console_unlock();
				console_unlock();
			return -1;
			return -1;
		}
		}
@@ -1501,8 +1525,10 @@ int radeon_resume_kms(struct drm_device *dev, bool resume)
	radeon_pm_resume(rdev);
	radeon_pm_resume(rdev);
	radeon_restore_bios_scratch_regs(rdev);
	radeon_restore_bios_scratch_regs(rdev);


	if (fbcon) {
		radeon_fbdev_set_suspend(rdev, 0);
		radeon_fbdev_set_suspend(rdev, 0);
		console_unlock();
		console_unlock();
	}
       
       
	/* init dig PHYs, disp eng pll */
	/* init dig PHYs, disp eng pll */
	if (rdev->is_atom_bios) {
	if (rdev->is_atom_bios) {
+46 −1
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@
#include "atom.h"
#include "atom.h"
#include <asm/div64.h>
#include <asm/div64.h>


#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_edid.h>


@@ -494,11 +495,55 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
	return r;
	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 = {
static const struct drm_crtc_funcs radeon_crtc_funcs = {
	.cursor_set = radeon_crtc_cursor_set,
	.cursor_set = radeon_crtc_cursor_set,
	.cursor_move = radeon_crtc_cursor_move,
	.cursor_move = radeon_crtc_cursor_move,
	.gamma_set = radeon_crtc_gamma_set,
	.gamma_set = radeon_crtc_gamma_set,
	.set_config = drm_crtc_helper_set_config,
	.set_config = radeon_crtc_set_config,
	.destroy = radeon_crtc_destroy,
	.destroy = radeon_crtc_destroy,
	.page_flip = radeon_crtc_page_flip,
	.page_flip = radeon_crtc_page_flip,
};
};
Loading