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