diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-06-12 23:23:42 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-08-09 15:11:08 -0400 |
commit | 7e1ee6333c32a4b83aad430a4bcb8a7057f36194 (patch) | |
tree | e3f921e98245fe9f7c2582fdf8c2e21a795ba25f /drivers/gpu | |
parent | d5d7a0fa742383406c84e5292d00eccdbf74d91a (diff) |
drm/nouveau/clk: allow selection of different power state for ac vs battery
v2:
- s/init/fini/ typo, reported by Alex
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/device/ctrl.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/subdev/clock.h | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/os.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/clock/base.c | 101 |
4 files changed, 96 insertions, 30 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c index 4b69bf56ed01..754fc1da6a0b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c | |||
@@ -41,7 +41,10 @@ nouveau_control_mthd_pstate_info(struct nouveau_object *object, u32 mthd, | |||
41 | 41 | ||
42 | if (clk) { | 42 | if (clk) { |
43 | args->count = clk->state_nr; | 43 | args->count = clk->state_nr; |
44 | args->ustate = clk->ustate; | 44 | if (clk->pwrsrc) |
45 | args->ustate = clk->ustate_ac; | ||
46 | else | ||
47 | args->ustate = clk->ustate_dc; | ||
45 | args->pstate = clk->pstate; | 48 | args->pstate = clk->pstate; |
46 | } else { | 49 | } else { |
47 | args->count = 0; | 50 | args->count = 0; |
@@ -123,7 +126,7 @@ nouveau_control_mthd_pstate_user(struct nouveau_object *object, u32 mthd, | |||
123 | if (size < sizeof(*args) || !clk) | 126 | if (size < sizeof(*args) || !clk) |
124 | return -EINVAL; | 127 | return -EINVAL; |
125 | 128 | ||
126 | return nouveau_clock_ustate(clk, args->state); | 129 | return nouveau_clock_ustate(clk, args->state, clk->pwrsrc); |
127 | } | 130 | } |
128 | 131 | ||
129 | struct nouveau_oclass | 132 | struct nouveau_oclass |
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h index 3168e1a0578d..9f37c096c34d 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h | |||
@@ -75,8 +75,11 @@ struct nouveau_clock { | |||
75 | wait_queue_head_t wait; | 75 | wait_queue_head_t wait; |
76 | atomic_t waiting; | 76 | atomic_t waiting; |
77 | 77 | ||
78 | struct nouveau_eventh *pwrsrc_ntfy; | ||
79 | int pwrsrc; | ||
78 | int pstate; /* current */ | 80 | int pstate; /* current */ |
79 | int ustate; /* user-requested (-1 disabled, -2 perfmon) */ | 81 | int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */ |
82 | int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */ | ||
80 | int astate; /* perfmon adjustment (base) */ | 83 | int astate; /* perfmon adjustment (base) */ |
81 | int tstate; /* thermal adjustment (max-) */ | 84 | int tstate; /* thermal adjustment (max-) */ |
82 | int dstate; /* display adjustment (min+) */ | 85 | int dstate; /* display adjustment (min+) */ |
@@ -122,15 +125,17 @@ struct nouveau_clocks { | |||
122 | struct nouveau_clock *clk = (p); \ | 125 | struct nouveau_clock *clk = (p); \ |
123 | _nouveau_clock_init(nv_object(clk)); \ | 126 | _nouveau_clock_init(nv_object(clk)); \ |
124 | }) | 127 | }) |
125 | #define nouveau_clock_fini(p,s) \ | 128 | #define nouveau_clock_fini(p,s) ({ \ |
126 | nouveau_subdev_fini(&(p)->base, (s)) | 129 | struct nouveau_clock *clk = (p); \ |
130 | _nouveau_clock_fini(nv_object(clk), (s)); \ | ||
131 | }) | ||
127 | 132 | ||
128 | int nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *, | 133 | int nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *, |
129 | struct nouveau_oclass *, | 134 | struct nouveau_oclass *, |
130 | struct nouveau_clocks *, bool, int, void **); | 135 | struct nouveau_clocks *, bool, int, void **); |
131 | void _nouveau_clock_dtor(struct nouveau_object *); | 136 | void _nouveau_clock_dtor(struct nouveau_object *); |
132 | int _nouveau_clock_init(struct nouveau_object *); | 137 | int _nouveau_clock_init(struct nouveau_object *); |
133 | #define _nouveau_clock_fini _nouveau_subdev_fini | 138 | int _nouveau_clock_fini(struct nouveau_object *, bool); |
134 | 139 | ||
135 | extern struct nouveau_oclass nv04_clock_oclass; | 140 | extern struct nouveau_oclass nv04_clock_oclass; |
136 | extern struct nouveau_oclass nv40_clock_oclass; | 141 | extern struct nouveau_oclass nv40_clock_oclass; |
@@ -149,7 +154,7 @@ int nv04_clock_pll_prog(struct nouveau_clock *, u32 reg1, | |||
149 | int nva3_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *, | 154 | int nva3_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *, |
150 | int clk, struct nouveau_pll_vals *); | 155 | int clk, struct nouveau_pll_vals *); |
151 | 156 | ||
152 | int nouveau_clock_ustate(struct nouveau_clock *, int req); | 157 | int nouveau_clock_ustate(struct nouveau_clock *, int req, int pwr); |
153 | int nouveau_clock_astate(struct nouveau_clock *, int req, int rel); | 158 | int nouveau_clock_astate(struct nouveau_clock *, int req, int rel); |
154 | int nouveau_clock_dstate(struct nouveau_clock *, int req, int rel); | 159 | int nouveau_clock_dstate(struct nouveau_clock *, int req, int rel); |
155 | int nouveau_clock_tstate(struct nouveau_clock *, int req, int rel); | 160 | int nouveau_clock_tstate(struct nouveau_clock *, int req, int rel); |
diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h index d0ced94ca54c..e50decfebf30 100644 --- a/drivers/gpu/drm/nouveau/core/os.h +++ b/drivers/gpu/drm/nouveau/core/os.h | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/log2.h> | 22 | #include <linux/log2.h> |
23 | #include <linux/pm_runtime.h> | 23 | #include <linux/pm_runtime.h> |
24 | #include <linux/power_supply.h> | ||
24 | 25 | ||
25 | #include <asm/unaligned.h> | 26 | #include <asm/unaligned.h> |
26 | 27 | ||
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c index 77966370e995..3ad848bbbbb0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c | |||
@@ -202,12 +202,15 @@ nouveau_pstate_work(struct work_struct *work) | |||
202 | 202 | ||
203 | if (!atomic_xchg(&clk->waiting, 0)) | 203 | if (!atomic_xchg(&clk->waiting, 0)) |
204 | return; | 204 | return; |
205 | clk->pwrsrc = power_supply_is_system_supplied(); | ||
205 | 206 | ||
206 | nv_trace(clk, "P %d U %d A %d T %d D %d\n", clk->pstate, | 207 | nv_trace(clk, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n", |
207 | clk->ustate, clk->astate, clk->tstate, clk->dstate); | 208 | clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, |
209 | clk->astate, clk->tstate, clk->dstate); | ||
208 | 210 | ||
209 | if (clk->state_nr && clk->ustate != -1) { | 211 | pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; |
210 | pstate = (clk->ustate < 0) ? clk->astate : clk->ustate; | 212 | if (clk->state_nr && pstate != -1) { |
213 | pstate = (pstate < 0) ? clk->astate : pstate; | ||
211 | pstate = min(pstate, clk->state_nr - 1 - clk->tstate); | 214 | pstate = min(pstate, clk->state_nr - 1 - clk->tstate); |
212 | pstate = max(pstate, clk->dstate); | 215 | pstate = max(pstate, clk->dstate); |
213 | } else { | 216 | } else { |
@@ -224,6 +227,7 @@ nouveau_pstate_work(struct work_struct *work) | |||
224 | } | 227 | } |
225 | 228 | ||
226 | wake_up_all(&clk->wait); | 229 | wake_up_all(&clk->wait); |
230 | nouveau_event_get(clk->pwrsrc_ntfy); | ||
227 | } | 231 | } |
228 | 232 | ||
229 | static int | 233 | static int |
@@ -381,17 +385,40 @@ nouveau_clock_ustate_update(struct nouveau_clock *clk, int req) | |||
381 | req = i; | 385 | req = i; |
382 | } | 386 | } |
383 | 387 | ||
384 | clk->ustate = req; | 388 | return req + 2; |
385 | return 0; | 389 | } |
390 | |||
391 | static int | ||
392 | nouveau_clock_nstate(struct nouveau_clock *clk, const char *mode, int arglen) | ||
393 | { | ||
394 | int ret = 1; | ||
395 | |||
396 | if (strncasecmpz(mode, "disabled", arglen)) { | ||
397 | char save = mode[arglen]; | ||
398 | long v; | ||
399 | |||
400 | ((char *)mode)[arglen] = '\0'; | ||
401 | if (!kstrtol(mode, 0, &v)) { | ||
402 | ret = nouveau_clock_ustate_update(clk, v); | ||
403 | if (ret < 0) | ||
404 | ret = 1; | ||
405 | } | ||
406 | ((char *)mode)[arglen] = save; | ||
407 | } | ||
408 | |||
409 | return ret - 2; | ||
386 | } | 410 | } |
387 | 411 | ||
388 | int | 412 | int |
389 | nouveau_clock_ustate(struct nouveau_clock *clk, int req) | 413 | nouveau_clock_ustate(struct nouveau_clock *clk, int req, int pwr) |
390 | { | 414 | { |
391 | int ret = nouveau_clock_ustate_update(clk, req); | 415 | int ret = nouveau_clock_ustate_update(clk, req); |
392 | if (ret) | 416 | if (ret >= 0) { |
393 | return ret; | 417 | if (ret -= 2, pwr) clk->ustate_ac = ret; |
394 | return nouveau_pstate_calc(clk, true); | 418 | else clk->ustate_dc = ret; |
419 | return nouveau_pstate_calc(clk, true); | ||
420 | } | ||
421 | return ret; | ||
395 | } | 422 | } |
396 | 423 | ||
397 | int | 424 | int |
@@ -424,9 +451,26 @@ nouveau_clock_dstate(struct nouveau_clock *clk, int req, int rel) | |||
424 | return nouveau_pstate_calc(clk, true); | 451 | return nouveau_pstate_calc(clk, true); |
425 | } | 452 | } |
426 | 453 | ||
454 | static int | ||
455 | nouveau_clock_pwrsrc(void *data, u32 mask, int type) | ||
456 | { | ||
457 | struct nouveau_clock *clk = data; | ||
458 | nouveau_pstate_calc(clk, false); | ||
459 | return NVKM_EVENT_DROP; | ||
460 | } | ||
461 | |||
427 | /****************************************************************************** | 462 | /****************************************************************************** |
428 | * subdev base class implementation | 463 | * subdev base class implementation |
429 | *****************************************************************************/ | 464 | *****************************************************************************/ |
465 | |||
466 | int | ||
467 | _nouveau_clock_fini(struct nouveau_object *object, bool suspend) | ||
468 | { | ||
469 | struct nouveau_clock *clk = (void *)object; | ||
470 | nouveau_event_put(clk->pwrsrc_ntfy); | ||
471 | return nouveau_subdev_fini(&clk->base, suspend); | ||
472 | } | ||
473 | |||
430 | int | 474 | int |
431 | _nouveau_clock_init(struct nouveau_object *object) | 475 | _nouveau_clock_init(struct nouveau_object *object) |
432 | { | 476 | { |
@@ -434,6 +478,10 @@ _nouveau_clock_init(struct nouveau_object *object) | |||
434 | struct nouveau_clocks *clock = clk->domains; | 478 | struct nouveau_clocks *clock = clk->domains; |
435 | int ret; | 479 | int ret; |
436 | 480 | ||
481 | ret = nouveau_subdev_init(&clk->base); | ||
482 | if (ret) | ||
483 | return ret; | ||
484 | |||
437 | memset(&clk->bstate, 0x00, sizeof(clk->bstate)); | 485 | memset(&clk->bstate, 0x00, sizeof(clk->bstate)); |
438 | INIT_LIST_HEAD(&clk->bstate.list); | 486 | INIT_LIST_HEAD(&clk->bstate.list); |
439 | clk->bstate.pstate = 0xff; | 487 | clk->bstate.pstate = 0xff; |
@@ -464,6 +512,8 @@ _nouveau_clock_dtor(struct nouveau_object *object) | |||
464 | struct nouveau_clock *clk = (void *)object; | 512 | struct nouveau_clock *clk = (void *)object; |
465 | struct nouveau_pstate *pstate, *temp; | 513 | struct nouveau_pstate *pstate, *temp; |
466 | 514 | ||
515 | nouveau_event_ref(NULL, &clk->pwrsrc_ntfy); | ||
516 | |||
467 | list_for_each_entry_safe(pstate, temp, &clk->states, head) { | 517 | list_for_each_entry_safe(pstate, temp, &clk->states, head) { |
468 | nouveau_pstate_del(pstate); | 518 | nouveau_pstate_del(pstate); |
469 | } | 519 | } |
@@ -492,7 +542,8 @@ nouveau_clock_create_(struct nouveau_object *parent, | |||
492 | 542 | ||
493 | INIT_LIST_HEAD(&clk->states); | 543 | INIT_LIST_HEAD(&clk->states); |
494 | clk->domains = clocks; | 544 | clk->domains = clocks; |
495 | clk->ustate = -1; | 545 | clk->ustate_ac = -1; |
546 | clk->ustate_dc = -1; | ||
496 | 547 | ||
497 | INIT_WORK(&clk->work, nouveau_pstate_work); | 548 | INIT_WORK(&clk->work, nouveau_pstate_work); |
498 | init_waitqueue_head(&clk->wait); | 549 | init_waitqueue_head(&clk->wait); |
@@ -505,20 +556,26 @@ nouveau_clock_create_(struct nouveau_object *parent, | |||
505 | 556 | ||
506 | clk->allow_reclock = allow_reclock; | 557 | clk->allow_reclock = allow_reclock; |
507 | 558 | ||
559 | ret = nouveau_event_new(device->ntfy, 1, NVKM_DEVICE_NTFY_POWER, | ||
560 | nouveau_clock_pwrsrc, clk, | ||
561 | &clk->pwrsrc_ntfy); | ||
562 | if (ret) | ||
563 | return ret; | ||
564 | |||
508 | mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen); | 565 | mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen); |
509 | if (mode) { | 566 | if (mode) { |
510 | if (!strncasecmpz(mode, "disabled", arglen)) { | 567 | clk->ustate_ac = nouveau_clock_nstate(clk, mode, arglen); |
511 | clk->ustate = -1; | 568 | clk->ustate_dc = nouveau_clock_nstate(clk, mode, arglen); |
512 | } else { | ||
513 | char save = mode[arglen]; | ||
514 | long v; | ||
515 | |||
516 | ((char *)mode)[arglen] = '\0'; | ||
517 | if (!kstrtol(mode, 0, &v)) | ||
518 | nouveau_clock_ustate_update(clk, v); | ||
519 | ((char *)mode)[arglen] = save; | ||
520 | } | ||
521 | } | 569 | } |
522 | 570 | ||
571 | mode = nouveau_stropt(device->cfgopt, "NvClkModeAC", &arglen); | ||
572 | if (mode) | ||
573 | clk->ustate_ac = nouveau_clock_nstate(clk, mode, arglen); | ||
574 | |||
575 | mode = nouveau_stropt(device->cfgopt, "NvClkModeDC", &arglen); | ||
576 | if (mode) | ||
577 | clk->ustate_dc = nouveau_clock_nstate(clk, mode, arglen); | ||
578 | |||
579 | |||
523 | return 0; | 580 | return 0; |
524 | } | 581 | } |