video: tegra: host: Move device data to nvhost_device
[linux-2.6.git] / drivers / video / tegra / host / gr3d / gr3d_t20.c
1 /*
2  * drivers/video/tegra/host/gr3d/gr3d_t20.c
3  *
4  * Tegra Graphics Host 3D for Tegra2
5  *
6  * Copyright (c) 2010-2011, NVIDIA Corporation.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22
23 #include "nvhost_hwctx.h"
24 #include "dev.h"
25 #include "host1x/host1x_channel.h"
26 #include "host1x/host1x_hardware.h"
27 #include "host1x/host1x_syncpt.h"
28 #include "gr3d.h"
29
30 #include <linux/slab.h>
31
32 static const struct hwctx_reginfo ctxsave_regs_3d_global[] = {
33         HWCTX_REGINFO(0xe00,    4, DIRECT),
34         HWCTX_REGINFO(0xe05,   30, DIRECT),
35         HWCTX_REGINFO(0xe25,    2, DIRECT),
36         HWCTX_REGINFO(0xe28,    2, DIRECT),
37         HWCTX_REGINFO(0x001,    2, DIRECT),
38         HWCTX_REGINFO(0x00c,   10, DIRECT),
39         HWCTX_REGINFO(0x100,   34, DIRECT),
40         HWCTX_REGINFO(0x124,    2, DIRECT),
41         HWCTX_REGINFO(0x200,    5, DIRECT),
42         HWCTX_REGINFO(0x205, 1024, INDIRECT),
43         HWCTX_REGINFO(0x207, 1024, INDIRECT),
44         HWCTX_REGINFO(0x209,    1, DIRECT),
45         HWCTX_REGINFO(0x300,   64, DIRECT),
46         HWCTX_REGINFO(0x343,   25, DIRECT),
47         HWCTX_REGINFO(0x363,    2, DIRECT),
48         HWCTX_REGINFO(0x400,   16, DIRECT),
49         HWCTX_REGINFO(0x411,    1, DIRECT),
50         HWCTX_REGINFO(0x500,    4, DIRECT),
51         HWCTX_REGINFO(0x520,   32, DIRECT),
52         HWCTX_REGINFO(0x540,   64, INDIRECT),
53         HWCTX_REGINFO(0x600,   16, INDIRECT_4X),
54         HWCTX_REGINFO(0x603,  128, INDIRECT),
55         HWCTX_REGINFO(0x608,    4, DIRECT),
56         HWCTX_REGINFO(0x60e,    1, DIRECT),
57         HWCTX_REGINFO(0x700,   64, INDIRECT),
58         HWCTX_REGINFO(0x710,   50, DIRECT),
59         HWCTX_REGINFO(0x800,   16, INDIRECT_4X),
60         HWCTX_REGINFO(0x803,  512, INDIRECT),
61         HWCTX_REGINFO(0x805,   64, INDIRECT),
62         HWCTX_REGINFO(0x820,   32, DIRECT),
63         HWCTX_REGINFO(0x900,   64, INDIRECT),
64         HWCTX_REGINFO(0x902,    2, DIRECT),
65         HWCTX_REGINFO(0xa02,   10, DIRECT),
66         HWCTX_REGINFO(0xe04,    1, DIRECT),
67         HWCTX_REGINFO(0xe2a,    1, DIRECT),
68 };
69
70 /* the same context save command sequence is used for all contexts. */
71 static phys_addr_t save_phys;
72 static unsigned int save_size;
73
74 #define SAVE_BEGIN_V0_SIZE 5
75 #define SAVE_DIRECT_V0_SIZE 3
76 #define SAVE_INDIRECT_V0_SIZE 5
77 #define SAVE_END_V0_SIZE 5
78 #define SAVE_INCRS 3
79 #define SAVE_THRESH_OFFSET 1
80 #define RESTORE_BEGIN_SIZE 4
81 #define RESTORE_DIRECT_SIZE 1
82 #define RESTORE_INDIRECT_SIZE 2
83 #define RESTORE_END_SIZE 1
84
85 struct save_info {
86         u32 *ptr;
87         unsigned int save_count;
88         unsigned int restore_count;
89         unsigned int save_incrs;
90         unsigned int restore_incrs;
91 };
92
93 static u32 *setup_restore_regs_v0(u32 *ptr,
94                         const struct hwctx_reginfo *regs,
95                         unsigned int nr_regs)
96 {
97         const struct hwctx_reginfo *rend = regs + nr_regs;
98
99         for ( ; regs != rend; ++regs) {
100                 u32 offset = regs->offset;
101                 u32 count = regs->count;
102                 u32 indoff = offset + 1;
103                 switch (regs->type) {
104                 case HWCTX_REGINFO_DIRECT:
105                         nvhost_3dctx_restore_direct(ptr, offset, count);
106                         ptr += RESTORE_DIRECT_SIZE;
107                         break;
108                 case HWCTX_REGINFO_INDIRECT_4X:
109                         ++indoff;
110                         /* fall through */
111                 case HWCTX_REGINFO_INDIRECT:
112                         nvhost_3dctx_restore_indirect(ptr,
113                                         offset, 0, indoff, count);
114                         ptr += RESTORE_INDIRECT_SIZE;
115                         break;
116                 }
117                 ptr += count;
118         }
119         return ptr;
120 }
121
122 static void setup_restore_v0(u32 *ptr)
123 {
124         nvhost_3dctx_restore_begin(ptr);
125         ptr += RESTORE_BEGIN_SIZE;
126
127         ptr = setup_restore_regs_v0(ptr,
128                         ctxsave_regs_3d_global,
129                         ARRAY_SIZE(ctxsave_regs_3d_global));
130
131         nvhost_3dctx_restore_end(ptr);
132
133         wmb();
134 }
135
136 /*** v0 saver ***/
137
138 static void save_push_v0(struct nvhost_cdma *cdma,
139                         struct nvhost_hwctx *ctx)
140 {
141         nvhost_cdma_push_gather(cdma,
142                         (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE,
143                         (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE,
144                         nvhost_opcode_gather(save_size),
145                         save_phys);
146 }
147
148 static void __init save_begin_v0(u32 *ptr)
149 {
150         /* 3d: when done, increment syncpt to base+1 */
151         ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
152         ptr[1] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE,
153                         NVSYNCPT_3D); /*  incr 1 */
154         /* host: wait for syncpt base+1 */
155         ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
156                                         NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
157         ptr[3] = nvhost_class_host_wait_syncpt_base(NVSYNCPT_3D,
158                                                 NVWAITBASE_3D, 1);
159         /* host: signal context read thread to start reading */
160         ptr[4] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE,
161                         NVSYNCPT_3D); /* incr 2 */
162 }
163
164 static void __init save_direct_v0(u32 *ptr, u32 start_reg, u32 count)
165 {
166         ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDOFF, 1);
167         ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
168                                                 start_reg, true);
169         ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count);
170 }
171
172 static void __init save_indirect_v0(u32 *ptr, u32 offset_reg, u32 offset,
173                         u32 data_reg, u32 count)
174 {
175         ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID,
176                                         offset_reg, 1);
177         ptr[1] = offset;
178         ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
179                                         NV_CLASS_HOST_INDOFF, 1);
180         ptr[3] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
181                                                 data_reg, false);
182         ptr[4] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count);
183 }
184
185 static void __init save_end_v0(u32 *ptr)
186 {
187         /* Wait for context read service to finish (cpu incr 3) */
188         ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
189         ptr[1] = nvhost_class_host_wait_syncpt_base(NVSYNCPT_3D,
190                         NVWAITBASE_3D, nvhost_3dctx_save_incrs);
191         /* Advance syncpoint base */
192         ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
193         ptr[3] = nvhost_class_host_incr_syncpt_base(NVWAITBASE_3D,
194                         nvhost_3dctx_save_incrs);
195         /* set class back to the unit */
196         ptr[4] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
197 }
198
199 static u32 *save_regs_v0(u32 *ptr, unsigned int *pending,
200                         void __iomem *chan_regs,
201                         const struct hwctx_reginfo *regs,
202                         unsigned int nr_regs)
203 {
204         const struct hwctx_reginfo *rend = regs + nr_regs;
205         int drain_result = 0;
206
207         for ( ; regs != rend; ++regs) {
208                 u32 count = regs->count;
209                 switch (regs->type) {
210                 case HWCTX_REGINFO_DIRECT:
211                         ptr += RESTORE_DIRECT_SIZE;
212                         break;
213                 case HWCTX_REGINFO_INDIRECT:
214                 case HWCTX_REGINFO_INDIRECT_4X:
215                         ptr += RESTORE_INDIRECT_SIZE;
216                         break;
217                 }
218                 drain_result = host1x_drain_read_fifo(chan_regs,
219                         ptr, count, pending);
220                 BUG_ON(drain_result < 0);
221                 ptr += count;
222         }
223         return ptr;
224 }
225
226 /*** save ***/
227
228 static void __init setup_save_regs(struct save_info *info,
229                         const struct hwctx_reginfo *regs,
230                         unsigned int nr_regs)
231 {
232         const struct hwctx_reginfo *rend = regs + nr_regs;
233         u32 *ptr = info->ptr;
234         unsigned int save_count = info->save_count;
235         unsigned int restore_count = info->restore_count;
236
237         for ( ; regs != rend; ++regs) {
238                 u32 offset = regs->offset;
239                 u32 count = regs->count;
240                 u32 indoff = offset + 1;
241                 switch (regs->type) {
242                 case HWCTX_REGINFO_DIRECT:
243                         if (ptr) {
244                                 save_direct_v0(ptr, offset, count);
245                                 ptr += SAVE_DIRECT_V0_SIZE;
246                         }
247                         save_count += SAVE_DIRECT_V0_SIZE;
248                         restore_count += RESTORE_DIRECT_SIZE;
249                         break;
250                 case HWCTX_REGINFO_INDIRECT_4X:
251                         ++indoff;
252                         /* fall through */
253                 case HWCTX_REGINFO_INDIRECT:
254                         if (ptr) {
255                                 save_indirect_v0(ptr, offset, 0,
256                                                 indoff, count);
257                                 ptr += SAVE_INDIRECT_V0_SIZE;
258                         }
259                         save_count += SAVE_INDIRECT_V0_SIZE;
260                         restore_count += RESTORE_INDIRECT_SIZE;
261                         break;
262                 }
263                 if (ptr) {
264                         /* SAVE cases only: reserve room for incoming data */
265                         u32 k = 0;
266                         /*
267                          * Create a signature pattern for indirect data (which
268                          * will be overwritten by true incoming data) for
269                          * better deducing where we are in a long command
270                          * sequence, when given only a FIFO snapshot for debug
271                          * purposes.
272                         */
273                         for (k = 0; k < count; k++)
274                                 *(ptr + k) = 0xd000d000 | (offset << 16) | k;
275                         ptr += count;
276                 }
277                 save_count += count;
278                 restore_count += count;
279         }
280
281         info->ptr = ptr;
282         info->save_count = save_count;
283         info->restore_count = restore_count;
284 }
285
286 static void __init setup_save(u32 *ptr)
287 {
288         struct save_info info = {
289                 ptr,
290                 SAVE_BEGIN_V0_SIZE,
291                 RESTORE_BEGIN_SIZE,
292                 SAVE_INCRS,
293                 1
294         };
295
296         if (info.ptr) {
297                 save_begin_v0(info.ptr);
298                 info.ptr += SAVE_BEGIN_V0_SIZE;
299         }
300
301         /* save regs */
302         setup_save_regs(&info,
303                         ctxsave_regs_3d_global,
304                         ARRAY_SIZE(ctxsave_regs_3d_global));
305
306         if (info.ptr) {
307                 save_end_v0(info.ptr);
308                 info.ptr += SAVE_END_V0_SIZE;
309         }
310
311         wmb();
312
313         save_size = info.save_count + SAVE_END_V0_SIZE;
314         nvhost_3dctx_restore_size = info.restore_count + RESTORE_END_SIZE;
315         nvhost_3dctx_save_incrs = info.save_incrs;
316         nvhost_3dctx_save_thresh =
317                         nvhost_3dctx_save_incrs - SAVE_THRESH_OFFSET;
318         nvhost_3dctx_restore_incrs = info.restore_incrs;
319 }
320
321
322
323 /*** ctx3d ***/
324
325 static struct nvhost_hwctx *ctx3d_alloc_v0(struct nvhost_channel *ch)
326 {
327         struct nvhost_hwctx *ctx = nvhost_3dctx_alloc_common(ch, true);
328         if (ctx)
329                 setup_restore_v0(ctx->restore_virt);
330         return ctx;
331 }
332
333 static void ctx3d_save_service(struct nvhost_hwctx *ctx)
334 {
335         u32 *ptr = (u32 *)ctx->restore_virt + RESTORE_BEGIN_SIZE;
336         unsigned int pending = 0;
337
338         ptr = save_regs_v0(ptr, &pending, ctx->channel->aperture,
339                         ctxsave_regs_3d_global,
340                         ARRAY_SIZE(ctxsave_regs_3d_global));
341
342         wmb();
343         nvhost_syncpt_cpu_incr(&ctx->channel->dev->host->syncpt, NVSYNCPT_3D);
344 }
345
346 int __init nvhost_gr3d_t20_ctxhandler_init(struct nvhost_hwctx_handler *h)
347 {
348         struct nvhost_channel *ch;
349         struct nvmap_client *nvmap;
350         u32 *save_ptr;
351
352         ch = container_of(h, struct nvhost_channel, ctxhandler);
353         nvmap = ch->dev->host->nvmap;
354
355         setup_save(NULL);
356
357         nvhost_3dctx_save_buf = nvmap_alloc(nvmap, save_size * sizeof(u32), 32,
358                                 NVMAP_HANDLE_WRITE_COMBINE);
359         if (IS_ERR(nvhost_3dctx_save_buf)) {
360                 int err = PTR_ERR(nvhost_3dctx_save_buf);
361                 nvhost_3dctx_save_buf = NULL;
362                 return err;
363         }
364
365         nvhost_3dctx_save_slots = 1;
366
367         save_ptr = nvmap_mmap(nvhost_3dctx_save_buf);
368         if (!save_ptr) {
369                 nvmap_free(nvmap, nvhost_3dctx_save_buf);
370                 nvhost_3dctx_save_buf = NULL;
371                 return -ENOMEM;
372         }
373
374         save_phys = nvmap_pin(nvmap, nvhost_3dctx_save_buf);
375
376         setup_save(save_ptr);
377
378         h->alloc = ctx3d_alloc_v0;
379         h->save_push = save_push_v0;
380         h->save_service = ctx3d_save_service;
381         h->get = nvhost_3dctx_get;
382         h->put = nvhost_3dctx_put;
383
384         return 0;
385 }