Commit | Line | Data |
---|---|---|
26fdd78c BS |
1 | /* |
2 | * Copyright 2013 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | |
23 | */ | |
24 | ||
fdb751ef BS |
25 | #include <nvif/os.h> |
26 | #include <nvif/class.h> | |
a532da97 | 27 | #include <nvif/ioctl.h> |
26fdd78c | 28 | |
fdb751ef | 29 | #include "nouveau_sysfs.h" |
26fdd78c | 30 | |
0d48b58a | 31 | MODULE_PARM_DESC(pstate, "enable sysfs pstate file, which will be moved in the future"); |
703fa264 | 32 | int nouveau_pstate; |
0d48b58a BS |
33 | module_param_named(pstate, nouveau_pstate, int, 0400); |
34 | ||
26fdd78c BS |
35 | static inline struct drm_device * |
36 | drm_device(struct device *d) | |
37 | { | |
420b9469 | 38 | return dev_get_drvdata(d); |
26fdd78c BS |
39 | } |
40 | ||
41 | #define snappendf(p,r,f,a...) do { \ | |
42 | snprintf(p, r, f, ##a); \ | |
43 | r -= strlen(p); \ | |
44 | p += strlen(p); \ | |
45 | } while(0) | |
46 | ||
47 | static ssize_t | |
48 | nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b) | |
49 | { | |
50 | struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d)); | |
a532da97 | 51 | struct nvif_control_pstate_info_v0 info = {}; |
26fdd78c BS |
52 | size_t cnt = PAGE_SIZE; |
53 | char *buf = b; | |
54 | int ret, i; | |
55 | ||
a532da97 | 56 | ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_INFO, |
0ad72863 | 57 | &info, sizeof(info)); |
26fdd78c BS |
58 | if (ret) |
59 | return ret; | |
60 | ||
61 | for (i = 0; i < info.count + 1; i++) { | |
62 | const s32 state = i < info.count ? i : | |
a532da97 BS |
63 | NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT; |
64 | struct nvif_control_pstate_attr_v0 attr = { | |
26fdd78c BS |
65 | .state = state, |
66 | .index = 0, | |
67 | }; | |
68 | ||
a532da97 | 69 | ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_ATTR, |
0ad72863 | 70 | &attr, sizeof(attr)); |
26fdd78c BS |
71 | if (ret) |
72 | return ret; | |
73 | ||
74 | if (i < info.count) | |
75 | snappendf(buf, cnt, "%02x:", attr.state); | |
76 | else | |
7238eca4 BS |
77 | snappendf(buf, cnt, "%s:", info.pwrsrc == 0 ? "DC" : |
78 | info.pwrsrc == 1 ? "AC" : | |
79 | "--"); | |
26fdd78c BS |
80 | |
81 | attr.index = 0; | |
82 | do { | |
83 | attr.state = state; | |
a532da97 BS |
84 | ret = nvif_mthd(&sysfs->ctrl, |
85 | NVIF_CONTROL_PSTATE_ATTR, | |
0ad72863 | 86 | &attr, sizeof(attr)); |
26fdd78c BS |
87 | if (ret) |
88 | return ret; | |
89 | ||
90 | snappendf(buf, cnt, " %s %d", attr.name, attr.min); | |
91 | if (attr.min != attr.max) | |
92 | snappendf(buf, cnt, "-%d", attr.max); | |
93 | snappendf(buf, cnt, " %s", attr.unit); | |
94 | } while (attr.index); | |
95 | ||
7238eca4 BS |
96 | if (state >= 0) { |
97 | if (info.ustate_ac == state) | |
98 | snappendf(buf, cnt, " AC"); | |
99 | if (info.ustate_dc == state) | |
100 | snappendf(buf, cnt, " DC"); | |
101 | if (info.pstate == state) | |
102 | snappendf(buf, cnt, " *"); | |
103 | } else { | |
104 | if (info.ustate_ac < -1) | |
105 | snappendf(buf, cnt, " AC"); | |
106 | if (info.ustate_dc < -1) | |
107 | snappendf(buf, cnt, " DC"); | |
108 | } | |
109 | ||
26fdd78c BS |
110 | snappendf(buf, cnt, "\n"); |
111 | } | |
112 | ||
113 | return strlen(b); | |
114 | } | |
115 | ||
116 | static ssize_t | |
117 | nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a, | |
118 | const char *buf, size_t count) | |
119 | { | |
120 | struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d)); | |
a532da97 | 121 | struct nvif_control_pstate_user_v0 args = { .pwrsrc = -EINVAL }; |
26fdd78c BS |
122 | long value, ret; |
123 | char *tmp; | |
124 | ||
125 | if ((tmp = strchr(buf, '\n'))) | |
126 | *tmp = '\0'; | |
127 | ||
7238eca4 BS |
128 | if (!strncasecmp(buf, "dc:", 3)) { |
129 | args.pwrsrc = 0; | |
130 | buf += 3; | |
131 | } else | |
132 | if (!strncasecmp(buf, "ac:", 3)) { | |
133 | args.pwrsrc = 1; | |
134 | buf += 3; | |
135 | } | |
136 | ||
26fdd78c | 137 | if (!strcasecmp(buf, "none")) |
a532da97 | 138 | args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN; |
26fdd78c BS |
139 | else |
140 | if (!strcasecmp(buf, "auto")) | |
a532da97 | 141 | args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON; |
26fdd78c BS |
142 | else { |
143 | ret = kstrtol(buf, 16, &value); | |
144 | if (ret) | |
145 | return ret; | |
7238eca4 | 146 | args.ustate = value; |
26fdd78c BS |
147 | } |
148 | ||
a532da97 | 149 | ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_USER, |
0ad72863 | 150 | &args, sizeof(args)); |
26fdd78c BS |
151 | if (ret < 0) |
152 | return ret; | |
153 | ||
154 | return count; | |
155 | } | |
156 | ||
157 | static DEVICE_ATTR(pstate, S_IRUGO | S_IWUSR, | |
158 | nouveau_sysfs_pstate_get, nouveau_sysfs_pstate_set); | |
159 | ||
160 | void | |
161 | nouveau_sysfs_fini(struct drm_device *dev) | |
162 | { | |
163 | struct nouveau_sysfs *sysfs = nouveau_sysfs(dev); | |
164 | struct nouveau_drm *drm = nouveau_drm(dev); | |
967e7bde | 165 | struct nvif_device *device = &drm->device; |
26fdd78c | 166 | |
0d48b58a | 167 | if (sysfs && sysfs->ctrl.priv) { |
989aa5b7 | 168 | device_remove_file(nv_device_base(nvxx_device(device)), &dev_attr_pstate); |
0ad72863 | 169 | nvif_object_fini(&sysfs->ctrl); |
26fdd78c BS |
170 | } |
171 | ||
172 | drm->sysfs = NULL; | |
173 | kfree(sysfs); | |
174 | } | |
175 | ||
176 | int | |
177 | nouveau_sysfs_init(struct drm_device *dev) | |
178 | { | |
179 | struct nouveau_drm *drm = nouveau_drm(dev); | |
967e7bde | 180 | struct nvif_device *device = &drm->device; |
26fdd78c BS |
181 | struct nouveau_sysfs *sysfs; |
182 | int ret; | |
183 | ||
0d48b58a BS |
184 | if (!nouveau_pstate) |
185 | return 0; | |
186 | ||
26fdd78c BS |
187 | sysfs = drm->sysfs = kzalloc(sizeof(*sysfs), GFP_KERNEL); |
188 | if (!sysfs) | |
189 | return -ENOMEM; | |
190 | ||
a01ca78c | 191 | ret = nvif_object_init(&device->object, NVDRM_CONTROL, |
a532da97 | 192 | NVIF_IOCTL_NEW_V0_CONTROL, NULL, 0, |
a01ca78c | 193 | &sysfs->ctrl); |
26fdd78c | 194 | if (ret == 0) |
989aa5b7 | 195 | device_create_file(nv_device_base(nvxx_device(device)), &dev_attr_pstate); |
26fdd78c BS |
196 | |
197 | return 0; | |
198 | } |