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

Commit 5addcf0a authored by Dave Airlie's avatar Dave Airlie
Browse files

nouveau: add runtime PM support (v0.9)



This hooks nouveau up to the runtime PM system to enable
dynamic power management for secondary GPUs in switchable
and optimus laptops.

a) rewrite suspend/resume printks to hide them during dynamic s/r
to avoid cluttering logs
b) add runtime pm suspend to irq handler, crtc display, ioctl handler,
connector status,
c) handle hdmi audio dynamic power on/off using magic register.

v0.5:
make sure we hit D3 properly
fix fbdev_set_suspend locking interaction, we only will poweroff if we have no
active crtcs/fbcon anyways.
add reference for active crtcs.
sprinkle mark last busy for autosuspend timeout

v0.6:
allow more flexible debugging - to avoid log spam
add option to enable/disable dynpm
got to D3Cold

v0.7:
add hdmi audio support.

v0.8:
call autosuspend from idle, so pci config space access doesn't go straight
back to sleep, this makes starting X faster.
only signal usage if we actually handle the irq, otherwise usb keeps us awake.
fix nv50 display active powerdown

v0.9:
use masking function to enable hdmi audio
set busy when we fail to suspend

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 13bb9cc8
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <core/subdev.h>
#include <core/printk.h>

int nv_printk_suspend_level = NV_DBG_DEBUG;

void
nv_printk_(struct nouveau_object *object, const char *pfx, int level,
	   const char *fmt, ...)
@@ -72,3 +74,20 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level,
	vprintk(mfmt, args);
	va_end(args);
}

#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x

const char *nv_printk_level_to_pfx(int level)
{
	switch (level) {
	CONV_LEVEL(FATAL);
	CONV_LEVEL(ERROR);
	CONV_LEVEL(WARN);
	CONV_LEVEL(INFO);
	CONV_LEVEL(DEBUG);
	CONV_LEVEL(PARANOIA);
	CONV_LEVEL(TRACE);
	CONV_LEVEL(SPAM);
	}
	return NV_PRINTK_DEBUG;
}
+13 −0
Original line number Diff line number Diff line
@@ -15,6 +15,12 @@ struct nouveau_object;
#define NV_PRINTK_TRACE    KERN_DEBUG
#define NV_PRINTK_SPAM     KERN_DEBUG

extern int nv_printk_suspend_level;

#define NV_DBG_SUSPEND (nv_printk_suspend_level)
#define NV_PRINTK_SUSPEND  (nv_printk_level_to_pfx(nv_printk_suspend_level))

const char *nv_printk_level_to_pfx(int level);
void __printf(4, 5)
nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);

@@ -31,6 +37,13 @@ nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)

#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a)

static inline void nv_suspend_set_printk_level(int level)
{
	nv_printk_suspend_level = level;
}

#define nv_assert(f,a...) do {                                                 \
	if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG)                              \
		nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a);  \
+1 −1
Original line number Diff line number Diff line
@@ -2165,7 +2165,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
	u16 data;

	if (execute)
		nv_info(bios, "running init tables\n");
		nv_suspend(bios, "running init tables\n");
	while (!ret && (data = (init_script(bios, ++i)))) {
		struct nvbios_init init = {
			.subdev = subdev,
+6 −0
Original line number Diff line number Diff line
@@ -23,16 +23,20 @@
 */

#include <subdev/mc.h>
#include <linux/pm_runtime.h>

static irqreturn_t
nouveau_mc_intr(int irq, void *arg)
{
	struct nouveau_mc *pmc = arg;
	const struct nouveau_mc_intr *map = pmc->intr_map;
	struct nouveau_device *device = nv_device(pmc);
	struct nouveau_subdev *unit;
	u32 stat, intr;

	intr = stat = nv_rd32(pmc, 0x000100);
	if (intr == 0xffffffff)
		return IRQ_NONE;
	while (stat && map->stat) {
		if (stat & map->stat) {
			unit = nouveau_subdev(pmc, map->unit);
@@ -47,6 +51,8 @@ nouveau_mc_intr(int irq, void *arg)
		nv_error(pmc, "unknown intr 0x%08x\n", stat);
	}

	if (stat == IRQ_HANDLED)
		pm_runtime_mark_last_busy(&device->pdev->dev);
	return stat ? IRQ_HANDLED : IRQ_NONE;
}

+48 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include <linux/pm_runtime.h>

#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -1007,13 +1008,59 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
	return 0;
}

int
nouveau_crtc_set_config(struct drm_mode_set *set)
{
	struct drm_device *dev;
	struct nouveau_drm *drm;
	int ret;
	struct drm_crtc *crtc;
	bool active = false;
	if (!set || !set->crtc)
		return -EINVAL;

	dev = set->crtc->dev;

	/* get a pm reference here */
	ret = pm_runtime_get_sync(dev->dev);
	if (ret < 0)
		return ret;

	ret = drm_crtc_helper_set_config(set);

	drm = nouveau_drm(dev);

	/* if we get here with no crtcs active then we can drop a reference */
	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
		if (crtc->enabled)
			active = true;
	}

	pm_runtime_mark_last_busy(dev->dev);
	/* if we have active crtcs and we don't have a power ref,
	   take the current one */
	if (active && !drm->have_disp_power_ref) {
		drm->have_disp_power_ref = true;
		return ret;
	}
	/* if we have no active crtcs, then drop the power ref
	   we got before */
	if (!active && drm->have_disp_power_ref) {
		pm_runtime_put_autosuspend(dev->dev);
		drm->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 nv04_crtc_funcs = {
	.save = nv_crtc_save,
	.restore = nv_crtc_restore,
	.cursor_set = nv04_crtc_cursor_set,
	.cursor_move = nv04_crtc_cursor_move,
	.gamma_set = nv_crtc_gamma_set,
	.set_config = drm_crtc_helper_set_config,
	.set_config = nouveau_crtc_set_config,
	.page_flip = nouveau_crtc_page_flip,
	.destroy = nv_crtc_destroy,
};
Loading