From: Daniel Vetter <daniel@ffwll.ch>
To: Dave Airlie <airlied@gmail.com>
Cc: dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 2/5] drm: Add initial dnyamic power off feature
Date: Mon, 10 Sep 2012 09:18:55 +0200 [thread overview]
Message-ID: <20120910071855.GA5387@phenom.ffwll.local> (raw)
In-Reply-To: <1347251515-10136-3-git-send-email-airlied@gmail.com>
On Mon, Sep 10, 2012 at 02:31:52PM +1000, Dave Airlie wrote:
> From: Dave Airlie <airlied@redhat.com>
>
> For secondary GPUs in laptops, i.e. optimus or powerxpress, we have
> methods for powering down the GPU completely. This adds support
> to the drm core for powering back up the GPU on any access from
> ioctls or sysfs interfaces, and fires a 5s timer to test if
> we can power the GPU off.
>
> This is just an initial implementation to get discussions started!
>
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> ---
> drivers/gpu/drm/drm_drv.c | 68 +++++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/drm_fb_helper.c | 2 +-
> drivers/gpu/drm/drm_fops.c | 6 +++-
> drivers/gpu/drm/drm_stub.c | 1 +
> drivers/gpu/drm/drm_sysfs.c | 4 +++
> include/drm/drmP.h | 9 ++++++
> 6 files changed, 88 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 9238de4..9fae62a 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -383,12 +383,17 @@ long drm_ioctl(struct file *filp,
> char stack_kdata[128];
> char *kdata = NULL;
> unsigned int usize, asize;
> + int ret;
>
> dev = file_priv->minor->dev;
>
> if (drm_device_is_unplugged(dev))
> return -ENODEV;
>
> + ret = drm_dynamic_power_wakeup(dev, __func__);
> + if (ret)
> + return ret;
> +
> atomic_inc(&dev->ioctl_count);
> atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
> ++file_priv->ioctl_count;
> @@ -494,3 +499,66 @@ struct drm_local_map *drm_getsarea(struct drm_device *dev)
> return NULL;
> }
> EXPORT_SYMBOL(drm_getsarea);
> +
> +#define POWER_OFF_PERIOD (5*HZ)
> +
> +static void drm_dynamic_enable_poll(struct drm_device *dev)
> +{
> + queue_delayed_work(system_nrt_wq, &dev->dynamic_power_poll, POWER_OFF_PERIOD);
> +}
> +
> +static void drm_power_poll_execute(struct work_struct *work)
> +{
> + struct delayed_work *delayed_work = to_delayed_work(work);
> + struct drm_device *dev = container_of(delayed_work, struct drm_device, dynamic_power_poll);
> + bool ret;
> +
> + /* ask driver if okay to power off */
My midlayer-smell-o-meter just cranked up to 11 when reading this comment
;-)
I'd have expected:
- Drivers to check the power state and enable the gpu if it's off in their
cs ioctl (instead of the brute-force every ioctl there is approach in
the drm core)
- Launch a delayed work item to cut the power again once they notice that
the gpu is idle (dunno how radeon/nouveau work exactly, but i915 has
this nice retire_requests work item which does a few similar things for
power management, like lvds downclocking)
- Same thing for any other kind of usage, like e.g. kms: Drivers can wrap the
crtc helper set_mode to ensure the gpu is on and also check for any
enabled outputs before launching the delayed power off switch. Same
applies to any sysfs/debugfs files (although in the case of i915.ko,
many of these don't need the hw to be on).
I guess you could add a small vga_switcheroo_dynamic_power_state struct or
something with a few helpers to do that to extract some duplicated code
from drivers. But tbh managing a piece of state lazily with a
timer/delayed work item is a common code pattern, so I don't think even
that little bit of code sharing is worth it.
Cheers, Daniel
PS: I've read a bit around in the switcheroo code and I think the
switcheroo ->can_switch callback is actually worse ... since the drm
drivers just check the open_count (which is hilariously racy in itself,
too) and there's no locking to ensure that stays the same between the
->can_switch check and the actual ->set_state calls ...
/me needs morning coffee to think this through
> + ret = dev->driver->dynamic_off_check(dev);
> + if (ret == false)
> + goto out_requeue;
> +
> + ret = dev->driver->dynamic_set_state(dev, DRM_SWITCH_POWER_DYNAMIC_OFF);
> + DRM_INFO("powering down\n");
> + return;
> +out_requeue:
> + queue_delayed_work(system_nrt_wq, delayed_work, POWER_OFF_PERIOD);
> +}
> +
> +int drm_dynamic_power_wakeup(struct drm_device *dev, const char *reason)
> +{
> + int ret;
> +
> + if (!dev->driver->dynamic_off_check)
> + return 0;
> +
> + cancel_delayed_work_sync(&dev->dynamic_power_poll);
> +
> + ret = mutex_lock_interruptible(&dev->dynamic_power_lock);
> + if (ret) {
> + drm_dynamic_enable_poll(dev);
> + return ret;
> + }
> +
> + if (dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) {
> + mutex_unlock(&dev->dynamic_power_lock);
> + drm_dynamic_enable_poll(dev);
> + return 0;
> + }
> +
> + DRM_INFO("waking up GPU for %s\n", reason);
> + ret = dev->driver->dynamic_set_state(dev, DRM_SWITCH_POWER_ON);
> + mutex_unlock(&dev->dynamic_power_lock);
> +
> + drm_dynamic_enable_poll(dev);
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dynamic_power_wakeup);
> +
> +void drm_dynamic_power_init(struct drm_device *dev)
> +{
> + INIT_DELAYED_WORK(&dev->dynamic_power_poll, drm_power_poll_execute);
> + if (dev->driver->dynamic_off_check)
> + drm_dynamic_enable_poll(dev);
> +}
> +EXPORT_SYMBOL(drm_dynamic_power_init);
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index f546d1e..9a2c56b 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -245,7 +245,7 @@ bool drm_fb_helper_force_kernel_mode(void)
> return false;
>
> list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
> - if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
> + if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF || helper->dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
> continue;
>
> ret = drm_fb_helper_restore_fbdev_mode(helper);
> diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
> index 5062eec..285e53f 100644
> --- a/drivers/gpu/drm/drm_fops.c
> +++ b/drivers/gpu/drm/drm_fops.c
> @@ -239,9 +239,13 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
> return -EBUSY; /* No exclusive opens */
> if (!drm_cpu_valid())
> return -EINVAL;
> - if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
> + if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
> return -EINVAL;
>
> + ret = drm_dynamic_power_wakeup(dev, __func__);
> + if (ret)
> + return ret;
> +
> DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id);
>
> priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
> index 21bcd4a..0e56a40 100644
> --- a/drivers/gpu/drm/drm_stub.c
> +++ b/drivers/gpu/drm/drm_stub.c
> @@ -273,6 +273,7 @@ int drm_fill_in_dev(struct drm_device *dev,
> spin_lock_init(&dev->event_lock);
> mutex_init(&dev->struct_mutex);
> mutex_init(&dev->ctxlist_mutex);
> + mutex_init(&dev->dynamic_power_lock);
>
> if (drm_ht_create(&dev->map_hash, 12)) {
> return -ENOMEM;
> diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
> index 45ac8d6..850c210 100644
> --- a/drivers/gpu/drm/drm_sysfs.c
> +++ b/drivers/gpu/drm/drm_sysfs.c
> @@ -162,6 +162,10 @@ static ssize_t status_show(struct device *device,
> enum drm_connector_status status;
> int ret;
>
> + ret = drm_dynamic_power_wakeup(connector->dev, __func__);
> + if (ret)
> + return ret;
> +
> ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex);
> if (ret)
> return ret;
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index d6b67bb..65154b0 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -933,6 +933,9 @@ struct drm_driver {
> struct drm_device *dev,
> uint32_t handle);
>
> + bool (*dynamic_off_check)(struct drm_device *dev);
> + int (*dynamic_set_state)(struct drm_device *dev, int state);
> +
> /* Driver private ops for this object */
> const struct vm_operations_struct *gem_vm_ops;
>
> @@ -1197,11 +1200,15 @@ struct drm_device {
> int switch_power_state;
>
> atomic_t unplugged; /* device has been unplugged or gone away */
> +
> + struct delayed_work dynamic_power_poll;
> + struct mutex dynamic_power_lock;
> };
>
> #define DRM_SWITCH_POWER_ON 0
> #define DRM_SWITCH_POWER_OFF 1
> #define DRM_SWITCH_POWER_CHANGING 2
> +#define DRM_SWITCH_POWER_DYNAMIC_OFF 3
>
> static __inline__ int drm_core_check_feature(struct drm_device *dev,
> int feature)
> @@ -1770,5 +1777,7 @@ static __inline__ bool drm_can_sleep(void)
> return true;
> }
>
> +void drm_dynamic_power_init(struct drm_device *dev);
> +int drm_dynamic_power_wakeup(struct drm_device *dev, const char *reason);
> #endif /* __KERNEL__ */
> #endif
> --
> 1.7.12
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
next prev parent reply other threads:[~2012-09-10 7:18 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-09-10 4:31 [RFC] drm dynamic power off support Dave Airlie
2012-09-10 4:31 ` [PATCH 1/5] gpu/vga_switcheroo: add driver control power feature Dave Airlie
2012-09-10 4:31 ` [PATCH 2/5] drm: Add initial dnyamic power off feature Dave Airlie
2012-09-10 7:18 ` Daniel Vetter [this message]
2012-09-10 8:23 ` Dave Airlie
2012-09-10 8:36 ` Chris Wilson
2012-09-10 10:55 ` Alan Cox
2012-09-10 9:00 ` Daniel Vetter
2012-09-10 11:07 ` Alan Cox
2012-09-10 11:16 ` Dave Airlie
2012-09-10 4:31 ` [PATCH 3/5] nouveau: Add interface to detect optimus support Dave Airlie
2012-09-10 16:25 ` Lekensteyn
2012-09-10 20:24 ` Dave Airlie
2012-09-10 4:31 ` [PATCH 4/5] nouveau: add dynamic gpu power off support Dave Airlie
2012-09-10 16:30 ` Peter Wu
2012-09-10 4:31 ` [PATCH 5/5] radeon: add dynamic " Dave Airlie
2012-09-10 5:04 ` [RFC] drm " Dave Airlie
2012-09-10 8:47 ` Takashi Iwai
2012-09-10 8:50 ` Dave Airlie
2012-09-11 13:32 ` Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120910071855.GA5387@phenom.ffwll.local \
--to=daniel@ffwll.ch \
--cc=airlied@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox