diff options
Diffstat (limited to 'drivers/video/tegra/host/gr3d/gr3d_t30.c')
-rw-r--r-- | drivers/video/tegra/host/gr3d/gr3d_t30.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c new file mode 100644 index 00000000000..e7329e50e3d --- /dev/null +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c | |||
@@ -0,0 +1,435 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/gr3d/gr3d_t30.c | ||
3 | * | ||
4 | * Tegra Graphics Host 3D for Tegra3 | ||
5 | * | ||
6 | * Copyright (c) 2011-2012 NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include "nvhost_hwctx.h" | ||
22 | #include "dev.h" | ||
23 | #include "host1x/host1x_hardware.h" | ||
24 | #include "host1x/host1x_syncpt.h" | ||
25 | #include "gr3d.h" | ||
26 | |||
27 | #include <mach/gpufuse.h> | ||
28 | #include <mach/hardware.h> | ||
29 | #include <linux/slab.h> | ||
30 | |||
31 | /* 99 > 2, which makes kernel panic if register set is incorrect */ | ||
32 | static int register_sets = 99; | ||
33 | |||
34 | static const struct hwctx_reginfo ctxsave_regs_3d_global[] = { | ||
35 | HWCTX_REGINFO(0xe00, 4, DIRECT), | ||
36 | HWCTX_REGINFO(0xe05, 30, DIRECT), | ||
37 | HWCTX_REGINFO(0xe25, 2, DIRECT), | ||
38 | HWCTX_REGINFO(0xe28, 2, DIRECT), | ||
39 | HWCTX_REGINFO(0xe30, 16, DIRECT), | ||
40 | HWCTX_REGINFO(0x001, 2, DIRECT), | ||
41 | HWCTX_REGINFO(0x00c, 10, DIRECT), | ||
42 | HWCTX_REGINFO(0x100, 34, DIRECT), | ||
43 | HWCTX_REGINFO(0x124, 2, DIRECT), | ||
44 | HWCTX_REGINFO(0x200, 5, DIRECT), | ||
45 | HWCTX_REGINFO(0x205, 1024, INDIRECT), | ||
46 | HWCTX_REGINFO(0x207, 1024, INDIRECT), | ||
47 | HWCTX_REGINFO(0x209, 1, DIRECT), | ||
48 | HWCTX_REGINFO(0x300, 64, DIRECT), | ||
49 | HWCTX_REGINFO(0x343, 25, DIRECT), | ||
50 | HWCTX_REGINFO(0x363, 2, DIRECT), | ||
51 | HWCTX_REGINFO(0x400, 16, DIRECT), | ||
52 | HWCTX_REGINFO(0x411, 1, DIRECT), | ||
53 | HWCTX_REGINFO(0x412, 1, DIRECT), | ||
54 | HWCTX_REGINFO(0x500, 4, DIRECT), | ||
55 | HWCTX_REGINFO(0x520, 32, DIRECT), | ||
56 | HWCTX_REGINFO(0x540, 64, INDIRECT), | ||
57 | HWCTX_REGINFO(0x600, 16, INDIRECT_4X), | ||
58 | HWCTX_REGINFO(0x603, 128, INDIRECT), | ||
59 | HWCTX_REGINFO(0x608, 4, DIRECT), | ||
60 | HWCTX_REGINFO(0x60e, 1, DIRECT), | ||
61 | HWCTX_REGINFO(0x700, 64, INDIRECT), | ||
62 | HWCTX_REGINFO(0x710, 50, DIRECT), | ||
63 | HWCTX_REGINFO(0x750, 16, DIRECT), | ||
64 | HWCTX_REGINFO(0x800, 16, INDIRECT_4X), | ||
65 | HWCTX_REGINFO(0x803, 512, INDIRECT), | ||
66 | HWCTX_REGINFO(0x805, 64, INDIRECT), | ||
67 | HWCTX_REGINFO(0x820, 32, DIRECT), | ||
68 | HWCTX_REGINFO(0x900, 64, INDIRECT), | ||
69 | HWCTX_REGINFO(0x902, 2, DIRECT), | ||
70 | HWCTX_REGINFO(0x90a, 1, DIRECT), | ||
71 | HWCTX_REGINFO(0xa02, 10, DIRECT), | ||
72 | HWCTX_REGINFO(0xb04, 1, DIRECT), | ||
73 | HWCTX_REGINFO(0xb06, 13, DIRECT), | ||
74 | HWCTX_REGINFO(0xe42, 2, DIRECT), /* HW bug workaround */ | ||
75 | }; | ||
76 | |||
77 | static const struct hwctx_reginfo ctxsave_regs_3d_perset[] = { | ||
78 | HWCTX_REGINFO(0xe04, 1, DIRECT), | ||
79 | HWCTX_REGINFO(0xe2a, 1, DIRECT), | ||
80 | HWCTX_REGINFO(0x413, 1, DIRECT), | ||
81 | HWCTX_REGINFO(0x90b, 1, DIRECT), | ||
82 | HWCTX_REGINFO(0xe41, 1, DIRECT), | ||
83 | }; | ||
84 | |||
85 | static unsigned int restore_set1_offset; | ||
86 | |||
87 | #define SAVE_BEGIN_V1_SIZE (1 + RESTORE_BEGIN_SIZE) | ||
88 | #define SAVE_DIRECT_V1_SIZE (4 + RESTORE_DIRECT_SIZE) | ||
89 | #define SAVE_INDIRECT_V1_SIZE (6 + RESTORE_INDIRECT_SIZE) | ||
90 | #define SAVE_END_V1_SIZE (9 + RESTORE_END_SIZE) | ||
91 | #define SAVE_INCRS 3 | ||
92 | #define SAVE_THRESH_OFFSET 0 | ||
93 | #define RESTORE_BEGIN_SIZE 4 | ||
94 | #define RESTORE_DIRECT_SIZE 1 | ||
95 | #define RESTORE_INDIRECT_SIZE 2 | ||
96 | #define RESTORE_END_SIZE 1 | ||
97 | |||
98 | struct save_info { | ||
99 | u32 *ptr; | ||
100 | unsigned int save_count; | ||
101 | unsigned int restore_count; | ||
102 | unsigned int save_incrs; | ||
103 | unsigned int restore_incrs; | ||
104 | }; | ||
105 | |||
106 | /*** v1 saver ***/ | ||
107 | |||
108 | static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) | ||
109 | { | ||
110 | struct host1x_hwctx *ctx = to_host1x_hwctx(nctx); | ||
111 | struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx); | ||
112 | |||
113 | /* wait for 3d idle */ | ||
114 | nvhost_cdma_push(cdma, | ||
115 | nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), | ||
116 | nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, | ||
117 | p->syncpt)); | ||
118 | nvhost_cdma_push(cdma, | ||
119 | nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
120 | NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), | ||
121 | nvhost_class_host_wait_syncpt_base(p->syncpt, | ||
122 | p->waitbase, 1)); | ||
123 | /* back to 3d */ | ||
124 | nvhost_cdma_push(cdma, | ||
125 | nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), | ||
126 | NVHOST_OPCODE_NOOP); | ||
127 | /* set register set 0 and 1 register read memory output addresses, | ||
128 | and send their reads to memory */ | ||
129 | if (register_sets == 2) { | ||
130 | nvhost_cdma_push(cdma, | ||
131 | nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, 2), | ||
132 | nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, | ||
133 | 1)); | ||
134 | nvhost_cdma_push(cdma, | ||
135 | nvhost_opcode_nonincr(0x904, 1), | ||
136 | ctx->restore_phys + restore_set1_offset * 4); | ||
137 | } | ||
138 | nvhost_cdma_push(cdma, | ||
139 | nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, 1), | ||
140 | nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 1)); | ||
141 | nvhost_cdma_push(cdma, | ||
142 | nvhost_opcode_nonincr(AR3D_DW_MEMORY_OUTPUT_ADDRESS, 1), | ||
143 | ctx->restore_phys); | ||
144 | /* gather the save buffer */ | ||
145 | nvhost_cdma_push_gather(cdma, | ||
146 | (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, | ||
147 | (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, | ||
148 | nvhost_opcode_gather(p->save_size), | ||
149 | p->save_phys); | ||
150 | } | ||
151 | |||
152 | static void __init save_begin_v1(struct host1x_hwctx_handler *p, u32 *ptr) | ||
153 | { | ||
154 | ptr[0] = nvhost_opcode_nonincr(AR3D_DW_MEMORY_OUTPUT_DATA, | ||
155 | RESTORE_BEGIN_SIZE); | ||
156 | nvhost_3dctx_restore_begin(p, ptr + 1); | ||
157 | ptr += RESTORE_BEGIN_SIZE; | ||
158 | } | ||
159 | |||
160 | static void __init save_direct_v1(u32 *ptr, u32 start_reg, u32 count) | ||
161 | { | ||
162 | ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, | ||
163 | AR3D_DW_MEMORY_OUTPUT_DATA, 1); | ||
164 | nvhost_3dctx_restore_direct(ptr + 1, start_reg, count); | ||
165 | ptr += RESTORE_DIRECT_SIZE; | ||
166 | ptr[1] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
167 | NV_CLASS_HOST_INDOFF, 1); | ||
168 | ptr[2] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, | ||
169 | start_reg, true); | ||
170 | /* TODO could do this in the setclass if count < 6 */ | ||
171 | ptr[3] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); | ||
172 | } | ||
173 | |||
174 | static void __init save_indirect_v1(u32 *ptr, u32 offset_reg, u32 offset, | ||
175 | u32 data_reg, u32 count) | ||
176 | { | ||
177 | ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0); | ||
178 | ptr[1] = nvhost_opcode_nonincr(AR3D_DW_MEMORY_OUTPUT_DATA, | ||
179 | RESTORE_INDIRECT_SIZE); | ||
180 | nvhost_3dctx_restore_indirect(ptr + 2, offset_reg, offset, data_reg, | ||
181 | count); | ||
182 | ptr += RESTORE_INDIRECT_SIZE; | ||
183 | ptr[2] = nvhost_opcode_imm(offset_reg, offset); | ||
184 | ptr[3] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
185 | NV_CLASS_HOST_INDOFF, 1); | ||
186 | ptr[4] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, | ||
187 | data_reg, false); | ||
188 | ptr[5] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); | ||
189 | } | ||
190 | |||
191 | static void __init save_end_v1(struct host1x_hwctx_handler *p, u32 *ptr) | ||
192 | { | ||
193 | /* write end of restore buffer */ | ||
194 | ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, | ||
195 | AR3D_DW_MEMORY_OUTPUT_DATA, 1); | ||
196 | nvhost_3dctx_restore_end(p, ptr + 1); | ||
197 | ptr += RESTORE_END_SIZE; | ||
198 | /* reset to dual reg if necessary */ | ||
199 | ptr[1] = nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, | ||
200 | (1 << register_sets) - 1); | ||
201 | /* op_done syncpt incr to flush FDC */ | ||
202 | ptr[2] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, p->syncpt); | ||
203 | /* host wait for that syncpt incr, and advance the wait base */ | ||
204 | ptr[3] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
205 | NV_CLASS_HOST_WAIT_SYNCPT_BASE, | ||
206 | nvhost_mask2( | ||
207 | NV_CLASS_HOST_WAIT_SYNCPT_BASE, | ||
208 | NV_CLASS_HOST_INCR_SYNCPT_BASE)); | ||
209 | ptr[4] = nvhost_class_host_wait_syncpt_base(p->syncpt, | ||
210 | p->waitbase, p->save_incrs - 1); | ||
211 | ptr[5] = nvhost_class_host_incr_syncpt_base(p->waitbase, | ||
212 | p->save_incrs); | ||
213 | /* set class back to 3d */ | ||
214 | ptr[6] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0); | ||
215 | /* send reg reads back to host */ | ||
216 | ptr[7] = nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 0); | ||
217 | /* final syncpt increment to release waiters */ | ||
218 | ptr[8] = nvhost_opcode_imm(0, p->syncpt); | ||
219 | } | ||
220 | |||
221 | /*** save ***/ | ||
222 | |||
223 | |||
224 | |||
225 | static void __init setup_save_regs(struct save_info *info, | ||
226 | const struct hwctx_reginfo *regs, | ||
227 | unsigned int nr_regs) | ||
228 | { | ||
229 | const struct hwctx_reginfo *rend = regs + nr_regs; | ||
230 | u32 *ptr = info->ptr; | ||
231 | unsigned int save_count = info->save_count; | ||
232 | unsigned int restore_count = info->restore_count; | ||
233 | |||
234 | for ( ; regs != rend; ++regs) { | ||
235 | u32 offset = regs->offset; | ||
236 | u32 count = regs->count; | ||
237 | u32 indoff = offset + 1; | ||
238 | switch (regs->type) { | ||
239 | case HWCTX_REGINFO_DIRECT: | ||
240 | if (ptr) { | ||
241 | save_direct_v1(ptr, offset, count); | ||
242 | ptr += SAVE_DIRECT_V1_SIZE; | ||
243 | } | ||
244 | save_count += SAVE_DIRECT_V1_SIZE; | ||
245 | restore_count += RESTORE_DIRECT_SIZE; | ||
246 | break; | ||
247 | case HWCTX_REGINFO_INDIRECT_4X: | ||
248 | ++indoff; | ||
249 | /* fall through */ | ||
250 | case HWCTX_REGINFO_INDIRECT: | ||
251 | if (ptr) { | ||
252 | save_indirect_v1(ptr, offset, 0, | ||
253 | indoff, count); | ||
254 | ptr += SAVE_INDIRECT_V1_SIZE; | ||
255 | } | ||
256 | save_count += SAVE_INDIRECT_V1_SIZE; | ||
257 | restore_count += RESTORE_INDIRECT_SIZE; | ||
258 | break; | ||
259 | } | ||
260 | if (ptr) { | ||
261 | /* SAVE cases only: reserve room for incoming data */ | ||
262 | u32 k = 0; | ||
263 | /* | ||
264 | * Create a signature pattern for indirect data (which | ||
265 | * will be overwritten by true incoming data) for | ||
266 | * better deducing where we are in a long command | ||
267 | * sequence, when given only a FIFO snapshot for debug | ||
268 | * purposes. | ||
269 | */ | ||
270 | for (k = 0; k < count; k++) | ||
271 | *(ptr + k) = 0xd000d000 | (offset << 16) | k; | ||
272 | ptr += count; | ||
273 | } | ||
274 | save_count += count; | ||
275 | restore_count += count; | ||
276 | } | ||
277 | |||
278 | info->ptr = ptr; | ||
279 | info->save_count = save_count; | ||
280 | info->restore_count = restore_count; | ||
281 | } | ||
282 | |||
283 | static void __init switch_gpu(struct save_info *info, | ||
284 | unsigned int save_src_set, | ||
285 | u32 save_dest_sets, | ||
286 | u32 restore_dest_sets) | ||
287 | { | ||
288 | if (info->ptr) { | ||
289 | info->ptr[0] = nvhost_opcode_setclass( | ||
290 | NV_GRAPHICS_3D_CLASS_ID, | ||
291 | AR3D_DW_MEMORY_OUTPUT_DATA, 1); | ||
292 | info->ptr[1] = nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, | ||
293 | restore_dest_sets); | ||
294 | info->ptr[2] = nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, | ||
295 | save_dest_sets); | ||
296 | info->ptr[3] = nvhost_opcode_imm(AR3D_GSHIM_READ_SELECT, | ||
297 | save_src_set); | ||
298 | info->ptr += 4; | ||
299 | } | ||
300 | info->save_count += 4; | ||
301 | info->restore_count += 1; | ||
302 | } | ||
303 | |||
304 | static void __init setup_save(struct host1x_hwctx_handler *p, u32 *ptr) | ||
305 | { | ||
306 | struct save_info info = { | ||
307 | ptr, | ||
308 | SAVE_BEGIN_V1_SIZE, | ||
309 | RESTORE_BEGIN_SIZE, | ||
310 | SAVE_INCRS, | ||
311 | 1 | ||
312 | }; | ||
313 | int save_end_size = SAVE_END_V1_SIZE; | ||
314 | |||
315 | BUG_ON(register_sets > 2); | ||
316 | |||
317 | if (info.ptr) { | ||
318 | save_begin_v1(p, info.ptr); | ||
319 | info.ptr += SAVE_BEGIN_V1_SIZE; | ||
320 | } | ||
321 | |||
322 | /* read from set0, write cmds through set0, restore to set0 and 1 */ | ||
323 | if (register_sets == 2) | ||
324 | switch_gpu(&info, 0, 1, 3); | ||
325 | |||
326 | /* save regs that are common to both sets */ | ||
327 | setup_save_regs(&info, | ||
328 | ctxsave_regs_3d_global, | ||
329 | ARRAY_SIZE(ctxsave_regs_3d_global)); | ||
330 | |||
331 | /* read from set 0, write cmds through set0, restore to set0 */ | ||
332 | if (register_sets == 2) | ||
333 | switch_gpu(&info, 0, 1, 1); | ||
334 | |||
335 | /* save set 0 specific regs */ | ||
336 | setup_save_regs(&info, | ||
337 | ctxsave_regs_3d_perset, | ||
338 | ARRAY_SIZE(ctxsave_regs_3d_perset)); | ||
339 | |||
340 | if (register_sets == 2) { | ||
341 | /* read from set1, write cmds through set1, restore to set1 */ | ||
342 | switch_gpu(&info, 1, 2, 2); | ||
343 | /* note offset at which set 1 restore starts */ | ||
344 | restore_set1_offset = info.restore_count; | ||
345 | /* save set 1 specific regs */ | ||
346 | setup_save_regs(&info, | ||
347 | ctxsave_regs_3d_perset, | ||
348 | ARRAY_SIZE(ctxsave_regs_3d_perset)); | ||
349 | } | ||
350 | |||
351 | /* read from set0, write cmds through set1, restore to set0 and 1 */ | ||
352 | if (register_sets == 2) | ||
353 | switch_gpu(&info, 0, 2, 3); | ||
354 | |||
355 | if (info.ptr) { | ||
356 | save_end_v1(p, info.ptr); | ||
357 | info.ptr += SAVE_END_V1_SIZE; | ||
358 | } | ||
359 | |||
360 | wmb(); | ||
361 | |||
362 | p->save_size = info.save_count + save_end_size; | ||
363 | p->restore_size = info.restore_count + RESTORE_END_SIZE; | ||
364 | p->save_incrs = info.save_incrs; | ||
365 | p->save_thresh = p->save_incrs - SAVE_THRESH_OFFSET; | ||
366 | p->restore_incrs = info.restore_incrs; | ||
367 | } | ||
368 | |||
369 | |||
370 | /*** ctx3d ***/ | ||
371 | |||
372 | static struct nvhost_hwctx *ctx3d_alloc_v1(struct nvhost_hwctx_handler *h, | ||
373 | struct nvhost_channel *ch) | ||
374 | { | ||
375 | struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h); | ||
376 | struct host1x_hwctx *ctx = nvhost_3dctx_alloc_common(p, ch, false); | ||
377 | |||
378 | if (ctx) | ||
379 | return &ctx->hwctx; | ||
380 | else | ||
381 | return NULL; | ||
382 | } | ||
383 | |||
384 | struct nvhost_hwctx_handler *__init nvhost_gr3d_t30_ctxhandler_init( | ||
385 | u32 syncpt, u32 waitbase, | ||
386 | struct nvhost_channel *ch) | ||
387 | { | ||
388 | struct nvmap_client *nvmap; | ||
389 | u32 *save_ptr; | ||
390 | struct host1x_hwctx_handler *p; | ||
391 | |||
392 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
393 | if (!p) | ||
394 | return NULL; | ||
395 | |||
396 | nvmap = nvhost_get_host(ch->dev)->nvmap; | ||
397 | |||
398 | register_sets = tegra_gpu_register_sets(); | ||
399 | BUG_ON(register_sets == 0 || register_sets > 2); | ||
400 | |||
401 | p->syncpt = syncpt; | ||
402 | p->waitbase = waitbase; | ||
403 | |||
404 | setup_save(p, NULL); | ||
405 | |||
406 | p->save_buf = nvmap_alloc(nvmap, p->save_size * 4, 32, | ||
407 | NVMAP_HANDLE_WRITE_COMBINE, 0); | ||
408 | if (IS_ERR(p->save_buf)) { | ||
409 | p->save_buf = NULL; | ||
410 | return NULL; | ||
411 | } | ||
412 | |||
413 | p->save_slots = 6; | ||
414 | if (register_sets == 2) | ||
415 | p->save_slots += 2; | ||
416 | |||
417 | save_ptr = nvmap_mmap(p->save_buf); | ||
418 | if (!save_ptr) { | ||
419 | nvmap_free(nvmap, p->save_buf); | ||
420 | p->save_buf = NULL; | ||
421 | return NULL; | ||
422 | } | ||
423 | |||
424 | p->save_phys = nvmap_pin(nvmap, p->save_buf); | ||
425 | |||
426 | setup_save(p, save_ptr); | ||
427 | |||
428 | p->h.alloc = ctx3d_alloc_v1; | ||
429 | p->h.save_push = save_push_v1; | ||
430 | p->h.save_service = NULL; | ||
431 | p->h.get = nvhost_3dctx_get; | ||
432 | p->h.put = nvhost_3dctx_put; | ||
433 | |||
434 | return &p->h; | ||
435 | } | ||