]>
Commit | Line | Data |
---|---|---|
dceef5d8 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 | |
23 | */ | |
24 | ||
25 | #include <subdev/bios.h> | |
26 | #include <core/mm.h> | |
20cdeaf9 BS |
27 | |
28 | #include "nv50.h" | |
dceef5d8 | 29 | |
75faef78 BS |
30 | struct nv50_ram { |
31 | struct nouveau_ram base; | |
32 | }; | |
33 | ||
dceef5d8 | 34 | void |
dedaa8f0 | 35 | __nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem *mem) |
dceef5d8 BS |
36 | { |
37 | struct nouveau_mm_node *this; | |
dceef5d8 | 38 | |
dceef5d8 BS |
39 | while (!list_empty(&mem->regions)) { |
40 | this = list_first_entry(&mem->regions, typeof(*this), rl_entry); | |
41 | ||
42 | list_del(&this->rl_entry); | |
43 | nouveau_mm_free(&pfb->vram, &this); | |
44 | } | |
45 | ||
46 | nouveau_mm_free(&pfb->tags, &mem->tag); | |
dedaa8f0 RS |
47 | } |
48 | ||
49 | void | |
50 | nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem) | |
51 | { | |
52 | struct nouveau_mem *mem = *pmem; | |
53 | ||
54 | *pmem = NULL; | |
55 | if (unlikely(mem == NULL)) | |
56 | return; | |
57 | ||
58 | mutex_lock(&pfb->base.mutex); | |
59 | __nv50_ram_put(pfb, mem); | |
dceef5d8 BS |
60 | mutex_unlock(&pfb->base.mutex); |
61 | ||
62 | kfree(mem); | |
63 | } | |
64 | ||
75faef78 | 65 | int |
dceef5d8 BS |
66 | nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, |
67 | u32 memtype, struct nouveau_mem **pmem) | |
68 | { | |
69 | struct nouveau_mm *heap = &pfb->vram; | |
70 | struct nouveau_mm *tags = &pfb->tags; | |
71 | struct nouveau_mm_node *r; | |
72 | struct nouveau_mem *mem; | |
73 | int comp = (memtype & 0x300) >> 8; | |
74 | int type = (memtype & 0x07f); | |
75 | int back = (memtype & 0x800); | |
76 | int min, max, ret; | |
77 | ||
78 | max = (size >> 12); | |
79 | min = ncmin ? (ncmin >> 12) : max; | |
80 | align >>= 12; | |
81 | ||
82 | mem = kzalloc(sizeof(*mem), GFP_KERNEL); | |
83 | if (!mem) | |
84 | return -ENOMEM; | |
85 | ||
86 | mutex_lock(&pfb->base.mutex); | |
87 | if (comp) { | |
88 | if (align == 16) { | |
89 | int n = (max >> 4) * comp; | |
90 | ||
91 | ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag); | |
92 | if (ret) | |
93 | mem->tag = NULL; | |
94 | } | |
95 | ||
96 | if (unlikely(!mem->tag)) | |
97 | comp = 0; | |
98 | } | |
99 | ||
100 | INIT_LIST_HEAD(&mem->regions); | |
101 | mem->memtype = (comp << 7) | type; | |
102 | mem->size = max; | |
103 | ||
104 | type = nv50_fb_memtype[type]; | |
105 | do { | |
106 | if (back) | |
107 | ret = nouveau_mm_tail(heap, type, max, min, align, &r); | |
108 | else | |
109 | ret = nouveau_mm_head(heap, type, max, min, align, &r); | |
110 | if (ret) { | |
111 | mutex_unlock(&pfb->base.mutex); | |
112 | pfb->ram->put(pfb, &mem); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | list_add_tail(&r->rl_entry, &mem->regions); | |
117 | max -= r->length; | |
118 | } while (max); | |
119 | mutex_unlock(&pfb->base.mutex); | |
120 | ||
121 | r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); | |
122 | mem->offset = (u64)r->offset << 12; | |
123 | *pmem = mem; | |
124 | return 0; | |
125 | } | |
126 | ||
127 | static u32 | |
128 | nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram) | |
129 | { | |
130 | int i, parts, colbits, rowbitsa, rowbitsb, banks; | |
131 | u64 rowsize, predicted; | |
132 | u32 r0, r4, rt, ru, rblock_size; | |
133 | ||
134 | r0 = nv_rd32(pfb, 0x100200); | |
135 | r4 = nv_rd32(pfb, 0x100204); | |
136 | rt = nv_rd32(pfb, 0x100250); | |
137 | ru = nv_rd32(pfb, 0x001540); | |
138 | nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); | |
139 | ||
140 | for (i = 0, parts = 0; i < 8; i++) { | |
141 | if (ru & (0x00010000 << i)) | |
142 | parts++; | |
143 | } | |
144 | ||
145 | colbits = (r4 & 0x0000f000) >> 12; | |
146 | rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; | |
147 | rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; | |
148 | banks = 1 << (((r4 & 0x03000000) >> 24) + 2); | |
149 | ||
150 | rowsize = parts * banks * (1 << colbits) * 8; | |
151 | predicted = rowsize << rowbitsa; | |
152 | if (r0 & 0x00000004) | |
153 | predicted += rowsize << rowbitsb; | |
154 | ||
155 | if (predicted != ram->size) { | |
156 | nv_warn(pfb, "memory controller reports %d MiB VRAM\n", | |
157 | (u32)(ram->size >> 20)); | |
158 | } | |
159 | ||
160 | rblock_size = rowsize; | |
161 | if (rt & 1) | |
162 | rblock_size *= 3; | |
163 | ||
164 | nv_debug(pfb, "rblock %d bytes\n", rblock_size); | |
165 | return rblock_size; | |
166 | } | |
167 | ||
75faef78 BS |
168 | int |
169 | nv50_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine, | |
170 | struct nouveau_oclass *oclass, int length, void **pobject) | |
dceef5d8 | 171 | { |
dceef5d8 BS |
172 | const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ |
173 | const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ | |
75faef78 BS |
174 | struct nouveau_bios *bios = nouveau_bios(parent); |
175 | struct nouveau_fb *pfb = nouveau_fb(parent); | |
176 | struct nouveau_ram *ram; | |
dceef5d8 BS |
177 | int ret; |
178 | ||
75faef78 BS |
179 | ret = nouveau_ram_create_(parent, engine, oclass, length, pobject); |
180 | ram = *pobject; | |
dceef5d8 BS |
181 | if (ret) |
182 | return ret; | |
183 | ||
184 | ram->size = nv_rd32(pfb, 0x10020c); | |
75faef78 | 185 | ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); |
dceef5d8 | 186 | |
75faef78 BS |
187 | switch (nv_rd32(pfb, 0x100714) & 0x00000007) { |
188 | case 0: ram->type = NV_MEM_TYPE_DDR1; break; | |
189 | case 1: | |
190 | if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) | |
191 | ram->type = NV_MEM_TYPE_DDR3; | |
192 | else | |
193 | ram->type = NV_MEM_TYPE_DDR2; | |
dceef5d8 | 194 | break; |
75faef78 BS |
195 | case 2: ram->type = NV_MEM_TYPE_GDDR3; break; |
196 | case 3: ram->type = NV_MEM_TYPE_GDDR4; break; | |
197 | case 4: ram->type = NV_MEM_TYPE_GDDR5; break; | |
dceef5d8 | 198 | default: |
dceef5d8 BS |
199 | break; |
200 | } | |
201 | ||
75faef78 BS |
202 | ret = nouveau_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) - |
203 | (rsvd_head + rsvd_tail), | |
204 | nv50_fb_vram_rblock(pfb, ram) >> 12); | |
205 | if (ret) | |
206 | return ret; | |
207 | ||
208 | ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; | |
209 | ram->tags = nv_rd32(pfb, 0x100320); | |
dceef5d8 BS |
210 | ram->get = nv50_ram_get; |
211 | ram->put = nv50_ram_put; | |
212 | return 0; | |
213 | } | |
214 | ||
75faef78 BS |
215 | static int |
216 | nv50_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
217 | struct nouveau_oclass *oclass, void *data, u32 datasize, | |
218 | struct nouveau_object **pobject) | |
219 | { | |
220 | struct nv50_ram *ram; | |
221 | int ret; | |
222 | ||
223 | ret = nv50_ram_create(parent, engine, oclass, &ram); | |
224 | *pobject = nv_object(ram); | |
225 | if (ret) | |
226 | return ret; | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
dceef5d8 BS |
231 | struct nouveau_oclass |
232 | nv50_ram_oclass = { | |
dceef5d8 | 233 | .ofuncs = &(struct nouveau_ofuncs) { |
75faef78 | 234 | .ctor = nv50_ram_ctor, |
dceef5d8 BS |
235 | .dtor = _nouveau_ram_dtor, |
236 | .init = _nouveau_ram_init, | |
237 | .fini = _nouveau_ram_fini, | |
238 | } | |
239 | }; |