Commit | Line | Data |
---|---|---|
02c30ca0 BS |
1 | /* |
2 | * Copyright 2010 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 | |
23 | */ | |
24 | ||
25 | #include "drmP.h" | |
26 | #include "nouveau_drv.h" | |
27 | #include "nouveau_pm.h" | |
28 | ||
29 | /*XXX: boards using limits 0x40 need fixing, the register layout | |
30 | * is correct here, but, there's some other funny magic | |
31 | * that modifies things, so it's not likely we'll set/read | |
32 | * the correct timings yet.. working on it... | |
33 | */ | |
34 | ||
35 | struct nv50_pm_state { | |
36 | struct pll_lims pll; | |
37 | enum pll_types type; | |
38 | int N, M, P; | |
39 | }; | |
40 | ||
41 | int | |
42 | nv50_pm_clock_get(struct drm_device *dev, u32 id) | |
43 | { | |
44 | struct pll_lims pll; | |
45 | int P, N, M, ret; | |
46 | u32 reg0, reg1; | |
47 | ||
48 | ret = get_pll_limits(dev, id, &pll); | |
49 | if (ret) | |
50 | return ret; | |
51 | ||
52 | if (pll.vco2.maxfreq) { | |
53 | reg0 = nv_rd32(dev, pll.reg + 0); | |
54 | reg1 = nv_rd32(dev, pll.reg + 4); | |
55 | P = (reg0 & 0x00070000) >> 16; | |
56 | N = (reg1 & 0x0000ff00) >> 8; | |
57 | M = (reg1 & 0x000000ff); | |
58 | ||
59 | return ((pll.refclk * N / M) >> P); | |
60 | } | |
61 | ||
62 | reg0 = nv_rd32(dev, pll.reg + 4); | |
63 | P = (reg0 & 0x003f0000) >> 16; | |
64 | N = (reg0 & 0x0000ff00) >> 8; | |
65 | M = (reg0 & 0x000000ff); | |
66 | return pll.refclk * N / M / P; | |
67 | } | |
68 | ||
69 | void * | |
70 | nv50_pm_clock_pre(struct drm_device *dev, u32 id, int khz) | |
71 | { | |
72 | struct nv50_pm_state *state; | |
73 | int dummy, ret; | |
74 | ||
75 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
76 | if (!state) | |
77 | return ERR_PTR(-ENOMEM); | |
78 | state->type = id; | |
79 | ||
80 | ret = get_pll_limits(dev, id, &state->pll); | |
81 | if (ret < 0) { | |
82 | kfree(state); | |
83 | return ERR_PTR(ret); | |
84 | } | |
85 | ||
86 | ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M, | |
87 | &dummy, &dummy, &state->P); | |
88 | if (ret < 0) { | |
89 | kfree(state); | |
90 | return ERR_PTR(ret); | |
91 | } | |
92 | ||
93 | return state; | |
94 | } | |
95 | ||
96 | void | |
97 | nv50_pm_clock_set(struct drm_device *dev, void *pre_state) | |
98 | { | |
99 | struct nv50_pm_state *state = pre_state; | |
100 | u32 reg = state->pll.reg, tmp; | |
101 | int N = state->N; | |
102 | int M = state->M; | |
103 | int P = state->P; | |
104 | ||
105 | if (state->pll.vco2.maxfreq) { | |
106 | if (state->type == PLL_MEMORY) { | |
107 | nv_wr32(dev, 0x100210, 0); | |
108 | nv_wr32(dev, 0x1002dc, 1); | |
109 | } | |
110 | ||
111 | tmp = nv_rd32(dev, reg + 0) & 0xfff8ffff; | |
112 | tmp |= 0x80000000 | (P << 16); | |
113 | nv_wr32(dev, reg + 0, tmp); | |
114 | nv_wr32(dev, reg + 4, (N << 8) | M); | |
115 | ||
116 | if (state->type == PLL_MEMORY) { | |
117 | nv_wr32(dev, 0x1002dc, 0); | |
118 | nv_wr32(dev, 0x100210, 0x80000000); | |
119 | } | |
120 | } else { | |
121 | nv_wr32(dev, reg + 4, (P << 16) | (N << 8) | M); | |
122 | } | |
123 | ||
124 | kfree(state); | |
125 | } | |
126 |