drm/nouveau: implement a simple sysfs interface to new pm code
authorBen Skeggs <bskeggs@redhat.com>
Tue, 15 Oct 2013 01:52:56 +0000 (11:52 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 8 Nov 2013 05:40:18 +0000 (15:40 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_sysfs.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_sysfs.h [new file with mode: 0644]

index e61dcbe09f0839b87b850889cfcf4a50e01e671f..64b4691b294a5645b5855386e1745d25e8f39442 100644 (file)
@@ -300,7 +300,7 @@ include $(src)/dispnv04/Makefile
 nouveau-y += nv50_display.o
 
 # drm/pm
-nouveau-y += nouveau_hwmon.o
+nouveau-y += nouveau_hwmon.o nouveau_sysfs.o
 
 # other random bits
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
index 9b3231459c7d42726e24070876c9ea16416b31ae..2418b0de589eb631b4fc7e3297c78bdce5557e45 100644 (file)
@@ -46,6 +46,7 @@
 #include "nouveau_gem.h"
 #include "nouveau_agp.h"
 #include "nouveau_vga.h"
+#include "nouveau_sysfs.h"
 #include "nouveau_hwmon.h"
 #include "nouveau_acpi.h"
 #include "nouveau_bios.h"
@@ -384,6 +385,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
                        goto fail_dispinit;
        }
 
+       nouveau_sysfs_init(dev);
        nouveau_hwmon_init(dev);
        nouveau_accel_init(drm);
        nouveau_fbcon_init(dev);
@@ -421,6 +423,7 @@ nouveau_drm_unload(struct drm_device *dev)
        nouveau_fbcon_fini(dev);
        nouveau_accel_fini(drm);
        nouveau_hwmon_fini(dev);
+       nouveau_sysfs_fini(dev);
 
        if (dev->mode_config.num_crtc)
                nouveau_display_fini(dev);
index e6d6a6b0053fd77fcb9a79186fd01b611630307a..71ed2dadae61cc7f6e1789fbf58b318538694d49 100644 (file)
@@ -51,10 +51,11 @@ struct nouveau_drm_tile {
 };
 
 enum nouveau_drm_handle {
-       NVDRM_CLIENT = 0xffffffff,
-       NVDRM_DEVICE = 0xdddddddd,
-       NVDRM_PUSH   = 0xbbbb0000, /* |= client chid */
-       NVDRM_CHAN   = 0xcccc0000, /* |= client chid */
+       NVDRM_CLIENT  = 0xffffffff,
+       NVDRM_DEVICE  = 0xdddddddd,
+       NVDRM_CONTROL = 0xdddddddc,
+       NVDRM_PUSH    = 0xbbbb0000, /* |= client chid */
+       NVDRM_CHAN    = 0xcccc0000, /* |= client chid */
 };
 
 struct nouveau_cli {
@@ -130,6 +131,7 @@ struct nouveau_drm {
 
        /* power management */
        struct nouveau_hwmon *hwmon;
+       struct nouveau_sysfs *sysfs;
 
        /* display power reference */
        bool have_disp_power_ref;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
new file mode 100644 (file)
index 0000000..89201a1
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nouveau_sysfs.h"
+
+#include <core/object.h>
+#include <core/class.h>
+
+static inline struct drm_device *
+drm_device(struct device *d)
+{
+       return pci_get_drvdata(to_pci_dev(d));
+}
+
+#define snappendf(p,r,f,a...) do {                                             \
+       snprintf(p, r, f, ##a);                                                \
+       r -= strlen(p);                                                        \
+       p += strlen(p);                                                        \
+} while(0)
+
+static ssize_t
+nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
+       struct nv_control_pstate_info info;
+       size_t cnt = PAGE_SIZE;
+       char *buf = b;
+       int ret, i;
+
+       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_INFO, &info, sizeof(info));
+       if (ret)
+               return ret;
+
+       for (i = 0; i < info.count + 1; i++) {
+               const s32 state = i < info.count ? i :
+                       NV_CONTROL_PSTATE_ATTR_STATE_CURRENT;
+               struct nv_control_pstate_attr attr = {
+                       .state = state,
+                       .index = 0,
+               };
+
+               ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
+                            &attr, sizeof(attr));
+               if (ret)
+                       return ret;
+
+               if (i < info.count)
+                       snappendf(buf, cnt, "%02x:", attr.state);
+               else
+                       snappendf(buf, cnt, "--:");
+
+               attr.index = 0;
+               do {
+                       attr.state = state;
+                       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
+                                    &attr, sizeof(attr));
+                       if (ret)
+                               return ret;
+
+                       snappendf(buf, cnt, " %s %d", attr.name, attr.min);
+                       if (attr.min != attr.max)
+                               snappendf(buf, cnt, "-%d", attr.max);
+                       snappendf(buf, cnt, " %s", attr.unit);
+               } while (attr.index);
+
+               if ((state >= 0 && info.pstate == state) ||
+                   (state <  0 && info.ustate < 0))
+                       snappendf(buf, cnt, " *");
+               snappendf(buf, cnt, "\n");
+       }
+
+       return strlen(b);
+}
+
+static ssize_t
+nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a,
+                        const char *buf, size_t count)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
+       struct nv_control_pstate_user args;
+       long value, ret;
+       char *tmp;
+
+       if ((tmp = strchr(buf, '\n')))
+               *tmp = '\0';
+
+       if (!strcasecmp(buf, "none"))
+               args.state = NV_CONTROL_PSTATE_USER_STATE_UNKNOWN;
+       else
+       if (!strcasecmp(buf, "auto"))
+               args.state = NV_CONTROL_PSTATE_USER_STATE_PERFMON;
+       else {
+               ret = kstrtol(buf, 16, &value);
+               if (ret)
+                       return ret;
+               args.state = value;
+       }
+
+       ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_USER, &args, sizeof(args));
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(pstate, S_IRUGO | S_IWUSR,
+                  nouveau_sysfs_pstate_get, nouveau_sysfs_pstate_set);
+
+void
+nouveau_sysfs_fini(struct drm_device *dev)
+{
+       struct nouveau_sysfs *sysfs = nouveau_sysfs(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+
+       if (sysfs->ctrl) {
+               device_remove_file(&dev->pdev->dev, &dev_attr_pstate);
+               nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL);
+       }
+
+       drm->sysfs = NULL;
+       kfree(sysfs);
+}
+
+int
+nouveau_sysfs_init(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_sysfs *sysfs;
+       int ret;
+
+       sysfs = drm->sysfs = kzalloc(sizeof(*sysfs), GFP_KERNEL);
+       if (!sysfs)
+               return -ENOMEM;
+
+       ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL,
+                                NV_CONTROL_CLASS, NULL, 0, &sysfs->ctrl);
+       if (ret == 0)
+               device_create_file(&dev->pdev->dev, &dev_attr_pstate);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.h b/drivers/gpu/drm/nouveau/nouveau_sysfs.h
new file mode 100644 (file)
index 0000000..74b47f1
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __NOUVEAU_SYSFS_H__
+#define __NOUVEAU_SYSFS_H__
+
+#include "nouveau_drm.h"
+
+struct nouveau_sysfs {
+       struct nouveau_object *ctrl;
+};
+
+static inline struct nouveau_sysfs *
+nouveau_sysfs(struct drm_device *dev)
+{
+       return nouveau_drm(dev)->sysfs;
+}
+
+int  nouveau_sysfs_init(struct drm_device *);
+void nouveau_sysfs_fini(struct drm_device *);
+
+#endif