Merge branch 'bcache-for-3.14' of git://evilpiepirate.org/~kent/linux-bcache into...
[cascardo/linux.git] / drivers / gpu / drm / nouveau / core / engine / device / ctrl.c
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
25 #include <core/object.h>
26 #include <core/class.h>
27
28 #include <subdev/clock.h>
29
30 #include "priv.h"
31
32 static int
33 nouveau_control_mthd_pstate_info(struct nouveau_object *object, u32 mthd,
34                                 void *data, u32 size)
35 {
36         struct nouveau_clock *clk = nouveau_clock(object);
37         struct nv_control_pstate_info *args = data;
38
39         if (size < sizeof(*args))
40                 return -EINVAL;
41
42         if (clk) {
43                 args->count  = clk->state_nr;
44                 args->ustate = clk->ustate;
45                 args->pstate = clk->pstate;
46         } else {
47                 args->count  = 0;
48                 args->ustate = NV_CONTROL_PSTATE_INFO_USTATE_DISABLE;
49                 args->pstate = NV_CONTROL_PSTATE_INFO_PSTATE_UNKNOWN;
50         }
51
52         return 0;
53 }
54
55 static int
56 nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
57                                 void *data, u32 size)
58 {
59         struct nouveau_clock *clk = nouveau_clock(object);
60         struct nv_control_pstate_attr *args = data;
61         struct nouveau_clocks *domain;
62         struct nouveau_pstate *pstate;
63         struct nouveau_cstate *cstate;
64         int i = 0, j = -1;
65         u32 lo, hi;
66
67         if ((size < sizeof(*args)) || !clk ||
68             (args->state >= 0 && args->state >= clk->state_nr))
69                 return -EINVAL;
70         domain = clk->domains;
71
72         while (domain->name != nv_clk_src_max) {
73                 if (domain->mname && ++j == args->index)
74                         break;
75                 domain++;
76         }
77
78         if (domain->name == nv_clk_src_max)
79                 return -EINVAL;
80
81         if (args->state != NV_CONTROL_PSTATE_ATTR_STATE_CURRENT) {
82                 list_for_each_entry(pstate, &clk->states, head) {
83                         if (i++ == args->state)
84                                 break;
85                 }
86
87                 lo = pstate->base.domain[domain->name];
88                 hi = lo;
89                 list_for_each_entry(cstate, &pstate->list, head) {
90                         lo = min(lo, cstate->domain[domain->name]);
91                         hi = max(hi, cstate->domain[domain->name]);
92                 }
93
94                 args->state = pstate->pstate;
95         } else {
96                 lo = max(clk->read(clk, domain->name), 0);
97                 hi = lo;
98         }
99
100         snprintf(args->name, sizeof(args->name), "%s", domain->mname);
101         snprintf(args->unit, sizeof(args->unit), "MHz");
102         args->min = lo / domain->mdiv;
103         args->max = hi / domain->mdiv;
104
105         args->index = 0;
106         while ((++domain)->name != nv_clk_src_max) {
107                 if (domain->mname) {
108                         args->index = ++j;
109                         break;
110                 }
111         }
112
113         return 0;
114 }
115
116 static int
117 nouveau_control_mthd_pstate_user(struct nouveau_object *object, u32 mthd,
118                                 void *data, u32 size)
119 {
120         struct nouveau_clock *clk = nouveau_clock(object);
121         struct nv_control_pstate_user *args = data;
122
123         if (size < sizeof(*args) || !clk)
124                 return -EINVAL;
125
126         return nouveau_clock_ustate(clk, args->state);
127 }
128
129 struct nouveau_oclass
130 nouveau_control_oclass[] = {
131         { .handle = NV_CONTROL_CLASS,
132           .ofuncs = &nouveau_object_ofuncs,
133           .omthds = (struct nouveau_omthds[]) {
134                   { NV_CONTROL_PSTATE_INFO,
135                     NV_CONTROL_PSTATE_INFO, nouveau_control_mthd_pstate_info },
136                   { NV_CONTROL_PSTATE_ATTR,
137                     NV_CONTROL_PSTATE_ATTR, nouveau_control_mthd_pstate_attr },
138                   { NV_CONTROL_PSTATE_USER,
139                     NV_CONTROL_PSTATE_USER, nouveau_control_mthd_pstate_user },
140                   {},
141           },
142         },
143         {}
144 };