aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/host/gr3d
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/host/gr3d
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/video/tegra/host/gr3d')
-rw-r--r--drivers/video/tegra/host/gr3d/Makefile10
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d.c211
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d.h54
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t20.c395
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t20.h30
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t30.c435
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t30.h30
-rw-r--r--drivers/video/tegra/host/gr3d/scale3d.c661
-rw-r--r--drivers/video/tegra/host/gr3d/scale3d.h47
9 files changed, 1873 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/gr3d/Makefile b/drivers/video/tegra/host/gr3d/Makefile
new file mode 100644
index 00000000000..dfbd078ab42
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/Makefile
@@ -0,0 +1,10 @@
1GCOV_PROFILE := y
2EXTRA_CFLAGS += -Idrivers/video/tegra/host
3
4nvhost-gr3d-objs = \
5 gr3d.o \
6 gr3d_t20.o \
7 gr3d_t30.o \
8 scale3d.o
9
10obj-$(CONFIG_TEGRA_GRHOST) += nvhost-gr3d.o
diff --git a/drivers/video/tegra/host/gr3d/gr3d.c b/drivers/video/tegra/host/gr3d/gr3d.c
new file mode 100644
index 00000000000..f387d54e585
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d.c
@@ -0,0 +1,211 @@
1/*
2 * drivers/video/tegra/host/gr3d/gr3d.c
3 *
4 * Tegra Graphics Host 3D
5 *
6 * Copyright (c) 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 <mach/nvmap.h>
22#include <linux/slab.h>
23
24#include "t20/t20.h"
25#include "host1x/host1x_channel.h"
26#include "host1x/host1x_hardware.h"
27#include "host1x/host1x_syncpt.h"
28#include "nvhost_hwctx.h"
29#include "dev.h"
30#include "gr3d.h"
31#include "bus_client.h"
32
33#ifndef TEGRA_POWERGATE_3D1
34#define TEGRA_POWERGATE_3D1 -1
35#endif
36
37void nvhost_3dctx_restore_begin(struct host1x_hwctx_handler *p, u32 *ptr)
38{
39 /* set class to host */
40 ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
41 NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
42 /* increment sync point base */
43 ptr[1] = nvhost_class_host_incr_syncpt_base(p->waitbase,
44 p->restore_incrs);
45 /* set class to 3D */
46 ptr[2] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
47 /* program PSEQ_QUAD_ID */
48 ptr[3] = nvhost_opcode_imm(AR3D_PSEQ_QUAD_ID, 0);
49}
50
51void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count)
52{
53 ptr[0] = nvhost_opcode_incr(start_reg, count);
54}
55
56void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg, u32 offset,
57 u32 data_reg, u32 count)
58{
59 ptr[0] = nvhost_opcode_imm(offset_reg, offset);
60 ptr[1] = nvhost_opcode_nonincr(data_reg, count);
61}
62
63void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *p, u32 *ptr)
64{
65 /* syncpt increment to track restore gather. */
66 ptr[0] = nvhost_opcode_imm_incr_syncpt(
67 NV_SYNCPT_OP_DONE, p->syncpt);
68}
69
70/*** ctx3d ***/
71
72struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p,
73 struct nvhost_channel *ch, bool map_restore)
74{
75 struct nvmap_client *nvmap = nvhost_get_host(ch->dev)->nvmap;
76 struct host1x_hwctx *ctx;
77
78 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
79 if (!ctx)
80 return NULL;
81 ctx->restore = nvmap_alloc(nvmap, p->restore_size * 4, 32,
82 map_restore ? NVMAP_HANDLE_WRITE_COMBINE
83 : NVMAP_HANDLE_UNCACHEABLE, 0);
84 if (IS_ERR_OR_NULL(ctx->restore))
85 goto fail;
86
87 if (map_restore) {
88 ctx->restore_virt = nvmap_mmap(ctx->restore);
89 if (!ctx->restore_virt)
90 goto fail;
91 } else
92 ctx->restore_virt = NULL;
93
94 kref_init(&ctx->hwctx.ref);
95 ctx->hwctx.h = &p->h;
96 ctx->hwctx.channel = ch;
97 ctx->hwctx.valid = false;
98 ctx->save_incrs = p->save_incrs;
99 ctx->save_thresh = p->save_thresh;
100 ctx->save_slots = p->save_slots;
101 ctx->restore_phys = nvmap_pin(nvmap, ctx->restore);
102 if (IS_ERR_VALUE(ctx->restore_phys))
103 goto fail;
104
105 ctx->restore_size = p->restore_size;
106 ctx->restore_incrs = p->restore_incrs;
107 return ctx;
108
109fail:
110 if (map_restore && ctx->restore_virt) {
111 nvmap_munmap(ctx->restore, ctx->restore_virt);
112 ctx->restore_virt = NULL;
113 }
114 nvmap_free(nvmap, ctx->restore);
115 ctx->restore = NULL;
116 kfree(ctx);
117 return NULL;
118}
119
120void nvhost_3dctx_get(struct nvhost_hwctx *ctx)
121{
122 kref_get(&ctx->ref);
123}
124
125void nvhost_3dctx_free(struct kref *ref)
126{
127 struct nvhost_hwctx *nctx = container_of(ref, struct nvhost_hwctx, ref);
128 struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
129 struct nvmap_client *nvmap =
130 nvhost_get_host(nctx->channel->dev)->nvmap;
131
132 if (ctx->restore_virt) {
133 nvmap_munmap(ctx->restore, ctx->restore_virt);
134 ctx->restore_virt = NULL;
135 }
136 nvmap_unpin(nvmap, ctx->restore);
137 ctx->restore_phys = 0;
138 nvmap_free(nvmap, ctx->restore);
139 ctx->restore = NULL;
140 kfree(ctx);
141}
142
143void nvhost_3dctx_put(struct nvhost_hwctx *ctx)
144{
145 kref_put(&ctx->ref, nvhost_3dctx_free);
146}
147
148int nvhost_gr3d_prepare_power_off(struct nvhost_device *dev)
149{
150 return host1x_save_context(dev, NVSYNCPT_3D);
151}
152
153static int __devinit gr3d_probe(struct nvhost_device *dev)
154{
155 return nvhost_client_device_init(dev);
156}
157
158static int __exit gr3d_remove(struct nvhost_device *dev)
159{
160 /* Add clean-up */
161 return 0;
162}
163
164static int gr3d_suspend(struct nvhost_device *dev, pm_message_t state)
165{
166 return nvhost_client_device_suspend(dev);
167}
168
169static int gr3d_resume(struct nvhost_device *dev)
170{
171 dev_info(&dev->dev, "resuming\n");
172 return 0;
173}
174
175struct nvhost_device *gr3d_device;
176
177static struct nvhost_driver gr3d_driver = {
178 .probe = gr3d_probe,
179 .remove = __exit_p(gr3d_remove),
180#ifdef CONFIG_PM
181 .suspend = gr3d_suspend,
182 .resume = gr3d_resume,
183#endif
184 .driver = {
185 .owner = THIS_MODULE,
186 .name = "gr3d",
187 }
188};
189
190static int __init gr3d_init(void)
191{
192 int err;
193
194 gr3d_device = nvhost_get_device("gr3d");
195 if (!gr3d_device)
196 return -ENXIO;
197
198 err = nvhost_device_register(gr3d_device);
199 if (err)
200 return err;
201
202 return nvhost_driver_register(&gr3d_driver);
203}
204
205static void __exit gr3d_exit(void)
206{
207 nvhost_driver_unregister(&gr3d_driver);
208}
209
210module_init(gr3d_init);
211module_exit(gr3d_exit);
diff --git a/drivers/video/tegra/host/gr3d/gr3d.h b/drivers/video/tegra/host/gr3d/gr3d.h
new file mode 100644
index 00000000000..3855b237b70
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d.h
@@ -0,0 +1,54 @@
1/*
2 * drivers/video/tegra/host/gr3d/gr3d.h
3 *
4 * Tegra Graphics Host 3D
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#ifndef __NVHOST_GR3D_GR3D_H
22#define __NVHOST_GR3D_GR3D_H
23
24#include "host1x/host1x_hwctx.h"
25#include <linux/types.h>
26
27/* Registers of 3D unit */
28
29#define AR3D_PSEQ_QUAD_ID 0x545
30#define AR3D_DW_MEMORY_OUTPUT_ADDRESS 0x904
31#define AR3D_DW_MEMORY_OUTPUT_DATA 0x905
32#define AR3D_GSHIM_WRITE_MASK 0xb00
33#define AR3D_GSHIM_READ_SELECT 0xb01
34#define AR3D_GLOBAL_MEMORY_OUTPUT_READS 0xe40
35
36struct nvhost_hwctx;
37struct nvhost_channel;
38struct kref;
39
40/* Functions used commonly by all 3D context switch modules */
41void nvhost_3dctx_restore_begin(struct host1x_hwctx_handler *h, u32 *ptr);
42void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count);
43void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg,
44 u32 offset, u32 data_reg, u32 count);
45void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *h, u32 *ptr);
46struct host1x_hwctx *nvhost_3dctx_alloc_common(
47 struct host1x_hwctx_handler *p,
48 struct nvhost_channel *ch, bool map_restore);
49void nvhost_3dctx_get(struct nvhost_hwctx *ctx);
50void nvhost_3dctx_free(struct kref *ref);
51void nvhost_3dctx_put(struct nvhost_hwctx *ctx);
52int nvhost_gr3d_prepare_power_off(struct nvhost_device *dev);
53
54#endif
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c
new file mode 100644
index 00000000000..3604142aaf2
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c
@@ -0,0 +1,395 @@
1/*
2 * drivers/video/tegra/host/gr3d/gr3d_t20.c
3 *
4 * Tegra Graphics Host 3D for Tegra2
5 *
6 * Copyright (c) 2010-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_channel.h"
24#include "host1x/host1x_hardware.h"
25#include "host1x/host1x_syncpt.h"
26#include "gr3d.h"
27
28#include <linux/slab.h>
29
30static const struct hwctx_reginfo ctxsave_regs_3d_global[] = {
31 HWCTX_REGINFO(0xe00, 4, DIRECT),
32 HWCTX_REGINFO(0xe05, 30, DIRECT),
33 HWCTX_REGINFO(0xe25, 2, DIRECT),
34 HWCTX_REGINFO(0xe28, 2, DIRECT),
35 HWCTX_REGINFO(0x001, 2, DIRECT),
36 HWCTX_REGINFO(0x00c, 10, DIRECT),
37 HWCTX_REGINFO(0x100, 34, DIRECT),
38 HWCTX_REGINFO(0x124, 2, DIRECT),
39 HWCTX_REGINFO(0x200, 5, DIRECT),
40 HWCTX_REGINFO(0x205, 1024, INDIRECT),
41 HWCTX_REGINFO(0x207, 1024, INDIRECT),
42 HWCTX_REGINFO(0x209, 1, DIRECT),
43 HWCTX_REGINFO(0x300, 64, DIRECT),
44 HWCTX_REGINFO(0x343, 25, DIRECT),
45 HWCTX_REGINFO(0x363, 2, DIRECT),
46 HWCTX_REGINFO(0x400, 16, DIRECT),
47 HWCTX_REGINFO(0x411, 1, DIRECT),
48 HWCTX_REGINFO(0x500, 4, DIRECT),
49 HWCTX_REGINFO(0x520, 32, DIRECT),
50 HWCTX_REGINFO(0x540, 64, INDIRECT),
51 HWCTX_REGINFO(0x600, 16, INDIRECT_4X),
52 HWCTX_REGINFO(0x603, 128, INDIRECT),
53 HWCTX_REGINFO(0x608, 4, DIRECT),
54 HWCTX_REGINFO(0x60e, 1, DIRECT),
55 HWCTX_REGINFO(0x700, 64, INDIRECT),
56 HWCTX_REGINFO(0x710, 50, DIRECT),
57 HWCTX_REGINFO(0x800, 16, INDIRECT_4X),
58 HWCTX_REGINFO(0x803, 512, INDIRECT),
59 HWCTX_REGINFO(0x805, 64, INDIRECT),
60 HWCTX_REGINFO(0x820, 32, DIRECT),
61 HWCTX_REGINFO(0x900, 64, INDIRECT),
62 HWCTX_REGINFO(0x902, 2, DIRECT),
63 HWCTX_REGINFO(0xa02, 10, DIRECT),
64 HWCTX_REGINFO(0xe04, 1, DIRECT),
65 HWCTX_REGINFO(0xe2a, 1, DIRECT),
66};
67
68/* the same context save command sequence is used for all contexts. */
69#define SAVE_BEGIN_V0_SIZE 5
70#define SAVE_DIRECT_V0_SIZE 3
71#define SAVE_INDIRECT_V0_SIZE 5
72#define SAVE_END_V0_SIZE 5
73#define SAVE_INCRS 3
74#define SAVE_THRESH_OFFSET 1
75#define RESTORE_BEGIN_SIZE 4
76#define RESTORE_DIRECT_SIZE 1
77#define RESTORE_INDIRECT_SIZE 2
78#define RESTORE_END_SIZE 1
79
80struct save_info {
81 u32 *ptr;
82 unsigned int save_count;
83 unsigned int restore_count;
84 unsigned int save_incrs;
85 unsigned int restore_incrs;
86};
87
88static u32 *setup_restore_regs_v0(u32 *ptr,
89 const struct hwctx_reginfo *regs,
90 unsigned int nr_regs)
91{
92 const struct hwctx_reginfo *rend = regs + nr_regs;
93
94 for ( ; regs != rend; ++regs) {
95 u32 offset = regs->offset;
96 u32 count = regs->count;
97 u32 indoff = offset + 1;
98 switch (regs->type) {
99 case HWCTX_REGINFO_DIRECT:
100 nvhost_3dctx_restore_direct(ptr, offset, count);
101 ptr += RESTORE_DIRECT_SIZE;
102 break;
103 case HWCTX_REGINFO_INDIRECT_4X:
104 ++indoff;
105 /* fall through */
106 case HWCTX_REGINFO_INDIRECT:
107 nvhost_3dctx_restore_indirect(ptr,
108 offset, 0, indoff, count);
109 ptr += RESTORE_INDIRECT_SIZE;
110 break;
111 }
112 ptr += count;
113 }
114 return ptr;
115}
116
117static void setup_restore_v0(struct host1x_hwctx_handler *h, u32 *ptr)
118{
119 nvhost_3dctx_restore_begin(h, ptr);
120 ptr += RESTORE_BEGIN_SIZE;
121
122 ptr = setup_restore_regs_v0(ptr,
123 ctxsave_regs_3d_global,
124 ARRAY_SIZE(ctxsave_regs_3d_global));
125
126 nvhost_3dctx_restore_end(h, ptr);
127
128 wmb();
129}
130
131/*** v0 saver ***/
132
133static void save_push_v0(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma)
134{
135 struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
136 struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx);
137
138 nvhost_cdma_push_gather(cdma,
139 (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE,
140 (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE,
141 nvhost_opcode_gather(p->save_size),
142 p->save_phys);
143}
144
145static void __init save_begin_v0(struct host1x_hwctx_handler *h, u32 *ptr)
146{
147 /* 3d: when done, increment syncpt to base+1 */
148 ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
149 ptr[1] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE,
150 h->syncpt); /* incr 1 */
151 /* host: wait for syncpt base+1 */
152 ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
153 NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
154 ptr[3] = nvhost_class_host_wait_syncpt_base(h->syncpt,
155 h->waitbase, 1);
156 /* host: signal context read thread to start reading */
157 ptr[4] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE,
158 h->syncpt); /* incr 2 */
159}
160
161static void __init save_direct_v0(u32 *ptr, u32 start_reg, u32 count)
162{
163 ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDOFF, 1);
164 ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
165 start_reg, true);
166 ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count);
167}
168
169static void __init save_indirect_v0(u32 *ptr, u32 offset_reg, u32 offset,
170 u32 data_reg, u32 count)
171{
172 ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID,
173 offset_reg, 1);
174 ptr[1] = offset;
175 ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
176 NV_CLASS_HOST_INDOFF, 1);
177 ptr[3] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
178 data_reg, false);
179 ptr[4] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count);
180}
181
182static void __init save_end_v0(struct host1x_hwctx_handler *h, u32 *ptr)
183{
184 /* Wait for context read service to finish (cpu incr 3) */
185 ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
186 ptr[1] = nvhost_class_host_wait_syncpt_base(h->syncpt,
187 h->waitbase, h->save_incrs);
188 /* Advance syncpoint base */
189 ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
190 ptr[3] = nvhost_class_host_incr_syncpt_base(NVWAITBASE_3D,
191 h->save_incrs);
192 /* set class back to the unit */
193 ptr[4] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
194}
195
196static u32 *save_regs_v0(u32 *ptr, unsigned int *pending,
197 void __iomem *chan_regs,
198 const struct hwctx_reginfo *regs,
199 unsigned int nr_regs)
200{
201 const struct hwctx_reginfo *rend = regs + nr_regs;
202 int drain_result = 0;
203
204 for ( ; regs != rend; ++regs) {
205 u32 count = regs->count;
206 switch (regs->type) {
207 case HWCTX_REGINFO_DIRECT:
208 ptr += RESTORE_DIRECT_SIZE;
209 break;
210 case HWCTX_REGINFO_INDIRECT:
211 case HWCTX_REGINFO_INDIRECT_4X:
212 ptr += RESTORE_INDIRECT_SIZE;
213 break;
214 }
215 drain_result = host1x_drain_read_fifo(chan_regs,
216 ptr, count, pending);
217 BUG_ON(drain_result < 0);
218 ptr += count;
219 }
220 return ptr;
221}
222
223/*** save ***/
224
225static 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_v0(ptr, offset, count);
242 ptr += SAVE_DIRECT_V0_SIZE;
243 }
244 save_count += SAVE_DIRECT_V0_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_v0(ptr, offset, 0,
253 indoff, count);
254 ptr += SAVE_INDIRECT_V0_SIZE;
255 }
256 save_count += SAVE_INDIRECT_V0_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
283static void __init setup_save(struct host1x_hwctx_handler *h, u32 *ptr)
284{
285 struct save_info info = {
286 ptr,
287 SAVE_BEGIN_V0_SIZE,
288 RESTORE_BEGIN_SIZE,
289 SAVE_INCRS,
290 1
291 };
292
293 if (info.ptr) {
294 save_begin_v0(h, info.ptr);
295 info.ptr += SAVE_BEGIN_V0_SIZE;
296 }
297
298 /* save regs */
299 setup_save_regs(&info,
300 ctxsave_regs_3d_global,
301 ARRAY_SIZE(ctxsave_regs_3d_global));
302
303 if (info.ptr) {
304 save_end_v0(h, info.ptr);
305 info.ptr += SAVE_END_V0_SIZE;
306 }
307
308 wmb();
309
310 h->save_size = info.save_count + SAVE_END_V0_SIZE;
311 h->restore_size = info.restore_count + RESTORE_END_SIZE;
312 h->save_incrs = info.save_incrs;
313 h->save_thresh = h->save_incrs - SAVE_THRESH_OFFSET;
314 h->restore_incrs = info.restore_incrs;
315}
316
317
318
319/*** ctx3d ***/
320
321static struct nvhost_hwctx *ctx3d_alloc_v0(struct nvhost_hwctx_handler *h,
322 struct nvhost_channel *ch)
323{
324 struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h);
325 struct host1x_hwctx *ctx =
326 nvhost_3dctx_alloc_common(p, ch, true);
327 if (ctx) {
328 setup_restore_v0(p, ctx->restore_virt);
329 return &ctx->hwctx;
330 } else
331 return NULL;
332}
333
334static void ctx3d_save_service(struct nvhost_hwctx *nctx)
335{
336 struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
337
338 u32 *ptr = (u32 *)ctx->restore_virt + RESTORE_BEGIN_SIZE;
339 unsigned int pending = 0;
340
341 ptr = save_regs_v0(ptr, &pending, nctx->channel->aperture,
342 ctxsave_regs_3d_global,
343 ARRAY_SIZE(ctxsave_regs_3d_global));
344
345 wmb();
346 nvhost_syncpt_cpu_incr(&nvhost_get_host(nctx->channel->dev)->syncpt,
347 host1x_hwctx_handler(ctx)->syncpt);
348}
349
350struct nvhost_hwctx_handler * __init nvhost_gr3d_t20_ctxhandler_init(
351 u32 syncpt, u32 waitbase,
352 struct nvhost_channel *ch)
353{
354 struct nvmap_client *nvmap;
355 u32 *save_ptr;
356 struct host1x_hwctx_handler *p;
357
358 p = kmalloc(sizeof(*p), GFP_KERNEL);
359 if (!p)
360 return NULL;
361 nvmap = nvhost_get_host(ch->dev)->nvmap;
362
363 p->syncpt = syncpt;
364 p->waitbase = waitbase;
365
366 setup_save(p, NULL);
367
368 p->save_buf = nvmap_alloc(nvmap, p->save_size * sizeof(u32), 32,
369 NVMAP_HANDLE_WRITE_COMBINE, 0);
370 if (IS_ERR(p->save_buf)) {
371 p->save_buf = NULL;
372 return NULL;
373 }
374
375 p->save_slots = 1;
376
377 save_ptr = nvmap_mmap(p->save_buf);
378 if (!save_ptr) {
379 nvmap_free(nvmap, p->save_buf);
380 p->save_buf = NULL;
381 return NULL;
382 }
383
384 p->save_phys = nvmap_pin(nvmap, p->save_buf);
385
386 setup_save(p, save_ptr);
387
388 p->h.alloc = ctx3d_alloc_v0;
389 p->h.save_push = save_push_v0;
390 p->h.save_service = ctx3d_save_service;
391 p->h.get = nvhost_3dctx_get;
392 p->h.put = nvhost_3dctx_put;
393
394 return &p->h;
395}
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.h b/drivers/video/tegra/host/gr3d/gr3d_t20.h
new file mode 100644
index 00000000000..5fe6d50d0c3
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d_t20.h
@@ -0,0 +1,30 @@
1/*
2 * drivers/video/tegra/host/gr3d/gr3d_t20.h
3 *
4 * Tegra Graphics Host 3D for Tegra2
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#ifndef __NVHOST_GR3D_GR3D_T20_H
22#define __NVHOST_GR3D_GR3D_T20_H
23
24struct nvhost_hwctx_handler;
25
26struct nvhost_hwctx_handler *nvhost_gr3d_t20_ctxhandler_init(
27 u32 syncpt, u32 waitbase,
28 struct nvhost_channel *ch);
29
30#endif
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 */
32static int register_sets = 99;
33
34static 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
77static 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
85static 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
98struct 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
108static 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
152static 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
160static 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
174static 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
191static 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
225static 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
283static 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
304static 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
372static 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
384struct 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}
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.h b/drivers/video/tegra/host/gr3d/gr3d_t30.h
new file mode 100644
index 00000000000..d1b787e14b4
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d_t30.h
@@ -0,0 +1,30 @@
1/*
2 * drivers/video/tegra/host/gr3d/gr3d_t30.h
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#ifndef __NVHOST_GR3D_GR3D_T30_H
22#define __NVHOST_GR3D_GR3D_T30_H
23
24struct nvhost_hwctx_handler;
25
26struct nvhost_hwctx_handler *nvhost_gr3d_t30_ctxhandler_init(
27 u32 syncpt, u32 waitbase,
28 struct nvhost_channel *ch);
29
30#endif
diff --git a/drivers/video/tegra/host/gr3d/scale3d.c b/drivers/video/tegra/host/gr3d/scale3d.c
new file mode 100644
index 00000000000..8a267a127ea
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/scale3d.c
@@ -0,0 +1,661 @@
1/*
2 * drivers/video/tegra/host/t20/scale3d.c
3 *
4 * Tegra Graphics Host 3D clock scaling
5 *
6 * Copyright (c) 2010-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/*
22 * 3d clock scaling
23 *
24 * module3d_notify_busy() is called upon submit, module3d_notify_idle() is
25 * called when all outstanding submits are completed. Idle times are measured
26 * over a fixed time period (scale3d.p_period). If the 3d module idle time
27 * percentage goes over the limit (set in scale3d.p_idle_max), 3d clocks are
28 * scaled down. If the percentage goes under the minimum limit (set in
29 * scale3d.p_idle_min), 3d clocks are scaled up. An additional test is made
30 * over the time frame given in scale3d.p_fast_response for clocking up
31 * quickly in response to load peaks.
32 *
33 * 3d.emc clock is scaled proportionately to 3d clock, with a quadratic-
34 * bezier-like factor added to pull 3d.emc rate a bit lower.
35 */
36
37#include <linux/debugfs.h>
38#include <linux/types.h>
39#include <linux/clk.h>
40#include <mach/clk.h>
41#include <mach/hardware.h>
42#include "scale3d.h"
43#include "dev.h"
44
45static int scale3d_is_enabled(void);
46static void scale3d_enable(int enable);
47
48#define POW2(x) ((x) * (x))
49
50/*
51 * debugfs parameters to control 3d clock scaling test
52 *
53 * period - time period for clock rate evaluation
54 * fast_response - time period for evaluation of 'busy' spikes
55 * idle_min - if less than [idle_min] percent idle over [fast_response]
56 * microseconds, clock up.
57 * idle_max - if over [idle_max] percent idle over [period] microseconds,
58 * clock down.
59 * max_scale - limits rate changes to no less than (100 - max_scale)% or
60 * (100 + 2 * max_scale)% of current clock rate
61 * verbosity - set above 5 for debug printouts
62 */
63
64struct scale3d_info_rec {
65 struct mutex lock; /* lock for timestamps etc */
66 int enable;
67 int init;
68 ktime_t idle_frame;
69 ktime_t fast_frame;
70 ktime_t last_idle;
71 ktime_t last_short_term_idle;
72 int is_idle;
73 ktime_t last_tweak;
74 ktime_t last_down;
75 int fast_up_count;
76 int slow_down_count;
77 int is_scaled;
78 int fast_responses;
79 unsigned long idle_total;
80 unsigned long idle_short_term_total;
81 unsigned long max_rate_3d;
82 long emc_slope;
83 long emc_offset;
84 long emc_dip_slope;
85 long emc_dip_offset;
86 long emc_xmid;
87 unsigned long min_rate_3d;
88 struct work_struct work;
89 struct delayed_work idle_timer;
90 unsigned int scale;
91 unsigned int p_period;
92 unsigned int period;
93 unsigned int p_idle_min;
94 unsigned int idle_min;
95 unsigned int p_idle_max;
96 unsigned int idle_max;
97 unsigned int p_fast_response;
98 unsigned int fast_response;
99 unsigned int p_adjust;
100 unsigned int p_scale_emc;
101 unsigned int p_emc_dip;
102 unsigned int p_verbosity;
103 struct clk *clk_3d;
104 struct clk *clk_3d2;
105 struct clk *clk_3d_emc;
106};
107
108static struct scale3d_info_rec scale3d;
109
110static void scale3d_clocks(unsigned long percent)
111{
112 unsigned long hz, curr;
113
114 if (!tegra_is_clk_enabled(scale3d.clk_3d))
115 return;
116
117 if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
118 if (!tegra_is_clk_enabled(scale3d.clk_3d2))
119 return;
120
121 curr = clk_get_rate(scale3d.clk_3d);
122 hz = percent * (curr / 100);
123
124 if (!(hz >= scale3d.max_rate_3d && curr == scale3d.max_rate_3d)) {
125 if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
126 clk_set_rate(scale3d.clk_3d2, 0);
127 clk_set_rate(scale3d.clk_3d, hz);
128
129 if (scale3d.p_scale_emc) {
130 long after = (long) clk_get_rate(scale3d.clk_3d);
131 hz = after * scale3d.emc_slope + scale3d.emc_offset;
132 if (scale3d.p_emc_dip)
133 hz -=
134 (scale3d.emc_dip_slope *
135 POW2(after / 1000 - scale3d.emc_xmid) +
136 scale3d.emc_dip_offset);
137 clk_set_rate(scale3d.clk_3d_emc, hz);
138 }
139 }
140}
141
142static void scale3d_clocks_handler(struct work_struct *work)
143{
144 unsigned int scale;
145
146 mutex_lock(&scale3d.lock);
147 scale = scale3d.scale;
148 mutex_unlock(&scale3d.lock);
149
150 if (scale != 0)
151 scale3d_clocks(scale);
152}
153
154void nvhost_scale3d_suspend(struct nvhost_device *dev)
155{
156 if (!scale3d.enable)
157 return;
158
159 cancel_work_sync(&scale3d.work);
160 cancel_delayed_work(&scale3d.idle_timer);
161}
162
163/* set 3d clocks to max */
164static void reset_3d_clocks(void)
165{
166 if (clk_get_rate(scale3d.clk_3d) != scale3d.max_rate_3d) {
167 clk_set_rate(scale3d.clk_3d, scale3d.max_rate_3d);
168 if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
169 clk_set_rate(scale3d.clk_3d2, scale3d.max_rate_3d);
170 if (scale3d.p_scale_emc)
171 clk_set_rate(scale3d.clk_3d_emc,
172 clk_round_rate(scale3d.clk_3d_emc, UINT_MAX));
173 }
174}
175
176static int scale3d_is_enabled(void)
177{
178 int enable;
179
180 if (!scale3d.enable)
181 return 0;
182
183 mutex_lock(&scale3d.lock);
184 enable = scale3d.enable;
185 mutex_unlock(&scale3d.lock);
186
187 return enable;
188}
189
190static void scale3d_enable(int enable)
191{
192 int disable = 0;
193
194 mutex_lock(&scale3d.lock);
195
196 if (enable) {
197 if (scale3d.max_rate_3d != scale3d.min_rate_3d)
198 scale3d.enable = 1;
199 } else {
200 scale3d.enable = 0;
201 disable = 1;
202 }
203
204 mutex_unlock(&scale3d.lock);
205
206 if (disable)
207 reset_3d_clocks();
208}
209
210static void reset_scaling_counters(ktime_t time)
211{
212 scale3d.idle_total = 0;
213 scale3d.idle_short_term_total = 0;
214 scale3d.last_idle = time;
215 scale3d.last_short_term_idle = time;
216 scale3d.idle_frame = time;
217}
218
219/* scaling_adjust - use scale up / scale down hint counts to adjust scaling
220 * parameters.
221 *
222 * hint_ratio is 100 x the ratio of scale up to scale down hints. Three cases
223 * are distinguished:
224 *
225 * hint_ratio < HINT_RATIO_MIN - set parameters to maximize scaling effect
226 * hint_ratio > HINT_RATIO_MAX - set parameters to minimize scaling effect
227 * hint_ratio between limits - scale parameters linearly
228 *
229 * the parameters adjusted are
230 *
231 * * fast_response time
232 * * period - time for scaling down estimate
233 * * idle_min percentage
234 * * idle_max percentage
235 */
236#define SCALING_ADJUST_PERIOD 1000000
237#define HINT_RATIO_MAX 400
238#define HINT_RATIO_MIN 100
239#define HINT_RATIO_MID ((HINT_RATIO_MAX + HINT_RATIO_MIN) / 2)
240#define HINT_RATIO_DIFF (HINT_RATIO_MAX - HINT_RATIO_MIN)
241
242static void scaling_adjust(ktime_t time)
243{
244 long hint_ratio;
245 long fast_response_adjustment;
246 long period_adjustment;
247 int idle_min_adjustment;
248 int idle_max_adjustment;
249 unsigned long dt;
250
251 dt = (unsigned long) ktime_us_delta(time, scale3d.last_tweak);
252 if (dt < SCALING_ADJUST_PERIOD)
253 return;
254
255 hint_ratio = (100 * (scale3d.fast_up_count + 1)) /
256 (scale3d.slow_down_count + 1);
257
258 if (hint_ratio > HINT_RATIO_MAX) {
259 fast_response_adjustment = -((int) scale3d.p_fast_response) / 4;
260 period_adjustment = scale3d.p_period / 2;
261 idle_min_adjustment = scale3d.p_idle_min;
262 idle_max_adjustment = scale3d.p_idle_max;
263 } else if (hint_ratio < HINT_RATIO_MIN) {
264 fast_response_adjustment = scale3d.p_fast_response / 2;
265 period_adjustment = -((int) scale3d.p_period) / 4;
266 idle_min_adjustment = -((int) scale3d.p_idle_min) / 2;
267 idle_max_adjustment = -((int) scale3d.p_idle_max) / 2;
268 } else {
269 int diff;
270 int factor;
271
272 diff = HINT_RATIO_MID - hint_ratio;
273 if (diff < 0)
274 factor = -diff * 2;
275 else {
276 factor = -diff;
277 diff *= 2;
278 }
279
280 fast_response_adjustment = diff *
281 (scale3d.p_fast_response / (HINT_RATIO_DIFF * 2));
282 period_adjustment =
283 diff * (scale3d.p_period / HINT_RATIO_DIFF);
284 idle_min_adjustment =
285 (factor * (int) scale3d.p_idle_min) / HINT_RATIO_DIFF;
286 idle_max_adjustment =
287 (factor * (int) scale3d.p_idle_max) / HINT_RATIO_DIFF;
288 }
289
290 scale3d.fast_response =
291 scale3d.p_fast_response + fast_response_adjustment;
292 scale3d.period = scale3d.p_period + period_adjustment;
293 scale3d.idle_min = scale3d.p_idle_min + idle_min_adjustment;
294 scale3d.idle_max = scale3d.p_idle_max + idle_max_adjustment;
295
296 if (scale3d.p_verbosity >= 10)
297 pr_info("scale3d stats: + %d - %d (/ %d) f %u p %u min %u max %u\n",
298 scale3d.fast_up_count, scale3d.slow_down_count,
299 scale3d.fast_responses, scale3d.fast_response,
300 scale3d.period, scale3d.idle_min, scale3d.idle_max);
301
302 scale3d.fast_up_count = 0;
303 scale3d.slow_down_count = 0;
304 scale3d.fast_responses = 0;
305 scale3d.last_down = time;
306 scale3d.last_tweak = time;
307}
308
309#undef SCALING_ADJUST_PERIOD
310#undef HINT_RATIO_MAX
311#undef HINT_RATIO_MIN
312#undef HINT_RATIO_MID
313#undef HINT_RATIO_DIFF
314
315static void scaling_state_check(ktime_t time)
316{
317 unsigned long dt;
318
319 /* adjustment: set scale parameters (fast_response, period) +/- 25%
320 * based on ratio of scale up to scale down hints
321 */
322 if (scale3d.p_adjust)
323 scaling_adjust(time);
324 else {
325 scale3d.fast_response = scale3d.p_fast_response;
326 scale3d.period = scale3d.p_period;
327 scale3d.idle_min = scale3d.p_idle_min;
328 scale3d.idle_max = scale3d.p_idle_max;
329 }
330
331 /* check for load peaks */
332 dt = (unsigned long) ktime_us_delta(time, scale3d.fast_frame);
333 if (dt > scale3d.fast_response) {
334 unsigned long idleness =
335 (scale3d.idle_short_term_total * 100) / dt;
336 scale3d.fast_responses++;
337 scale3d.fast_frame = time;
338 /* if too busy, scale up */
339 if (idleness < scale3d.idle_min) {
340 scale3d.is_scaled = 0;
341 scale3d.fast_up_count++;
342 if (scale3d.p_verbosity >= 5)
343 pr_info("scale3d: %ld%% busy\n",
344 100 - idleness);
345
346 reset_3d_clocks();
347 reset_scaling_counters(time);
348 return;
349 }
350 scale3d.idle_short_term_total = 0;
351 scale3d.last_short_term_idle = time;
352 }
353
354 dt = (unsigned long) ktime_us_delta(time, scale3d.idle_frame);
355 if (dt > scale3d.period) {
356 unsigned long idleness = (scale3d.idle_total * 100) / dt;
357
358 if (scale3d.p_verbosity >= 5)
359 pr_info("scale3d: idle %lu, ~%lu%%\n",
360 scale3d.idle_total, idleness);
361
362 if (idleness > scale3d.idle_max) {
363 if (!scale3d.is_scaled) {
364 scale3d.is_scaled = 1;
365 scale3d.last_down = time;
366 }
367 scale3d.slow_down_count++;
368 /* if idle time is high, clock down */
369 scale3d.scale = 100 - (idleness - scale3d.idle_min);
370 schedule_work(&scale3d.work);
371 }
372
373 reset_scaling_counters(time);
374 }
375}
376
377void nvhost_scale3d_notify_idle(struct nvhost_device *dev)
378{
379 ktime_t t;
380 unsigned long dt;
381
382 if (!scale3d.enable)
383 return;
384
385 mutex_lock(&scale3d.lock);
386
387 t = ktime_get();
388
389 if (scale3d.is_idle) {
390 dt = ktime_us_delta(t, scale3d.last_idle);
391 scale3d.idle_total += dt;
392 dt = ktime_us_delta(t, scale3d.last_short_term_idle);
393 scale3d.idle_short_term_total += dt;
394 } else
395 scale3d.is_idle = 1;
396
397 scale3d.last_idle = t;
398 scale3d.last_short_term_idle = t;
399
400 scaling_state_check(scale3d.last_idle);
401
402 /* delay idle_max % of 2 * fast_response time (given in microseconds) */
403 schedule_delayed_work(&scale3d.idle_timer,
404 msecs_to_jiffies((scale3d.idle_max * scale3d.fast_response)
405 / 50000));
406
407 mutex_unlock(&scale3d.lock);
408}
409
410void nvhost_scale3d_notify_busy(struct nvhost_device *dev)
411{
412 unsigned long idle;
413 unsigned long short_term_idle;
414 ktime_t t;
415
416 if (!scale3d.enable)
417 return;
418
419 mutex_lock(&scale3d.lock);
420
421 cancel_delayed_work(&scale3d.idle_timer);
422
423 t = ktime_get();
424
425 if (scale3d.is_idle) {
426 idle = (unsigned long)
427 ktime_us_delta(t, scale3d.last_idle);
428 scale3d.idle_total += idle;
429 short_term_idle =
430 ktime_us_delta(t, scale3d.last_short_term_idle);
431 scale3d.idle_short_term_total += short_term_idle;
432 scale3d.is_idle = 0;
433 }
434
435 scaling_state_check(t);
436
437 mutex_unlock(&scale3d.lock);
438}
439
440static void scale3d_idle_handler(struct work_struct *work)
441{
442 int notify_idle = 0;
443
444 if (!scale3d.enable)
445 return;
446
447 mutex_lock(&scale3d.lock);
448
449 if (scale3d.is_idle && tegra_is_clk_enabled(scale3d.clk_3d)) {
450 unsigned long curr = clk_get_rate(scale3d.clk_3d);
451 if (curr > scale3d.min_rate_3d)
452 notify_idle = 1;
453 }
454
455 mutex_unlock(&scale3d.lock);
456
457 if (notify_idle)
458 nvhost_scale3d_notify_idle(NULL);
459}
460
461void nvhost_scale3d_reset()
462{
463 ktime_t t;
464
465 if (!scale3d.enable)
466 return;
467
468 t = ktime_get();
469 mutex_lock(&scale3d.lock);
470 reset_scaling_counters(t);
471 mutex_unlock(&scale3d.lock);
472}
473
474/*
475 * debugfs parameters to control 3d clock scaling
476 */
477
478void nvhost_scale3d_debug_init(struct dentry *de)
479{
480 struct dentry *d, *f;
481
482 d = debugfs_create_dir("scaling", de);
483 if (!d) {
484 pr_err("scale3d: can\'t create debugfs directory\n");
485 return;
486 }
487
488#define CREATE_SCALE3D_FILE(fname) \
489 do {\
490 f = debugfs_create_u32(#fname, S_IRUGO | S_IWUSR, d,\
491 &scale3d.p_##fname);\
492 if (NULL == f) {\
493 pr_err("scale3d: can\'t create file " #fname "\n");\
494 return;\
495 } \
496 } while (0)
497
498 CREATE_SCALE3D_FILE(fast_response);
499 CREATE_SCALE3D_FILE(idle_min);
500 CREATE_SCALE3D_FILE(idle_max);
501 CREATE_SCALE3D_FILE(period);
502 CREATE_SCALE3D_FILE(adjust);
503 CREATE_SCALE3D_FILE(scale_emc);
504 CREATE_SCALE3D_FILE(emc_dip);
505 CREATE_SCALE3D_FILE(verbosity);
506#undef CREATE_SCALE3D_FILE
507}
508
509static ssize_t enable_3d_scaling_show(struct device *device,
510 struct device_attribute *attr, char *buf)
511{
512 ssize_t res;
513
514 res = snprintf(buf, PAGE_SIZE, "%d\n", scale3d_is_enabled());
515
516 return res;
517}
518
519static ssize_t enable_3d_scaling_store(struct device *dev,
520 struct device_attribute *attr, const char *buf, size_t count)
521{
522 unsigned long val = 0;
523
524 if (strict_strtoul(buf, 10, &val) < 0)
525 return -EINVAL;
526
527 scale3d_enable(val);
528
529 return count;
530}
531
532static DEVICE_ATTR(enable_3d_scaling, S_IRUGO | S_IWUSR,
533 enable_3d_scaling_show, enable_3d_scaling_store);
534
535void nvhost_scale3d_init(struct nvhost_device *d)
536{
537 if (!scale3d.init) {
538 int error;
539 unsigned long max_emc, min_emc;
540 long correction;
541 mutex_init(&scale3d.lock);
542
543 scale3d.clk_3d = d->clk[0];
544 if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) {
545 scale3d.clk_3d2 = d->clk[1];
546 scale3d.clk_3d_emc = d->clk[2];
547 } else
548 scale3d.clk_3d_emc = d->clk[1];
549
550 scale3d.max_rate_3d = clk_round_rate(scale3d.clk_3d, UINT_MAX);
551 scale3d.min_rate_3d = clk_round_rate(scale3d.clk_3d, 0);
552
553 if (scale3d.max_rate_3d == scale3d.min_rate_3d) {
554 pr_warn("scale3d: 3d max rate = min rate (%lu), "
555 "disabling\n", scale3d.max_rate_3d);
556 scale3d.enable = 0;
557 return;
558 }
559
560 /* emc scaling:
561 *
562 * Remc = S * R3d + O - (Sd * (R3d - Rm)^2 + Od)
563 *
564 * Remc - 3d.emc rate
565 * R3d - 3d.cbus rate
566 * Rm - 3d.cbus 'middle' rate = (max + min)/2
567 * S - emc_slope
568 * O - emc_offset
569 * Sd - emc_dip_slope
570 * Od - emc_dip_offset
571 *
572 * this superposes a quadratic dip centered around the middle 3d
573 * frequency over a linear correlation of 3d.emc to 3d clock
574 * rates.
575 *
576 * S, O are chosen so that the maximum 3d rate produces the
577 * maximum 3d.emc rate exactly, and the minimum 3d rate produces
578 * at least the minimum 3d.emc rate.
579 *
580 * Sd and Od are chosen to produce the largest dip that will
581 * keep 3d.emc frequencies monotonously decreasing with 3d
582 * frequencies. To achieve this, the first derivative of Remc
583 * with respect to R3d should be zero for the minimal 3d rate:
584 *
585 * R'emc = S - 2 * Sd * (R3d - Rm)
586 * R'emc(R3d-min) = 0
587 * S = 2 * Sd * (R3d-min - Rm)
588 * = 2 * Sd * (R3d-min - R3d-max) / 2
589 * Sd = S / (R3d-min - R3d-max)
590 *
591 * +---------------------------------------------------+
592 * | Sd = -(emc-max - emc-min) / (R3d-min - R3d-max)^2 |
593 * +---------------------------------------------------+
594 *
595 * dip = Sd * (R3d - Rm)^2 + Od
596 *
597 * requiring dip(R3d-min) = 0 and dip(R3d-max) = 0 gives
598 *
599 * Sd * (R3d-min - Rm)^2 + Od = 0
600 * Od = -Sd * ((R3d-min - R3d-max) / 2)^2
601 * = -Sd * ((R3d-min - R3d-max)^2) / 4
602 *
603 * +------------------------------+
604 * | Od = (emc-max - emc-min) / 4 |
605 * +------------------------------+
606 */
607
608 max_emc = clk_round_rate(scale3d.clk_3d_emc, UINT_MAX);
609 min_emc = clk_round_rate(scale3d.clk_3d_emc, 0);
610
611 scale3d.emc_slope = (max_emc - min_emc) /
612 (scale3d.max_rate_3d - scale3d.min_rate_3d);
613 scale3d.emc_offset = max_emc -
614 scale3d.emc_slope * scale3d.max_rate_3d;
615 /* guarantee max 3d rate maps to max emc rate */
616 scale3d.emc_offset += max_emc -
617 (scale3d.emc_slope * scale3d.max_rate_3d +
618 scale3d.emc_offset);
619
620 scale3d.emc_dip_offset = (max_emc - min_emc) / 4;
621 scale3d.emc_dip_slope =
622 -4 * (scale3d.emc_dip_offset /
623 (POW2(scale3d.max_rate_3d - scale3d.min_rate_3d)));
624 scale3d.emc_xmid =
625 (scale3d.max_rate_3d + scale3d.min_rate_3d) / 2;
626 correction =
627 scale3d.emc_dip_offset +
628 scale3d.emc_dip_slope *
629 POW2(scale3d.max_rate_3d - scale3d.emc_xmid);
630 scale3d.emc_dip_offset -= correction;
631
632 INIT_WORK(&scale3d.work, scale3d_clocks_handler);
633 INIT_DELAYED_WORK(&scale3d.idle_timer, scale3d_idle_handler);
634
635 /* set scaling parameter defaults */
636 scale3d.enable = 1;
637 scale3d.period = scale3d.p_period = 100000;
638 scale3d.idle_min = scale3d.p_idle_min = 10;
639 scale3d.idle_max = scale3d.p_idle_max = 15;
640 scale3d.fast_response = scale3d.p_fast_response = 7000;
641 scale3d.p_scale_emc = 1;
642 scale3d.p_emc_dip = 1;
643 scale3d.p_verbosity = 0;
644 scale3d.p_adjust = 1;
645
646 error = device_create_file(&d->dev,
647 &dev_attr_enable_3d_scaling);
648 if (error)
649 dev_err(&d->dev, "failed to create sysfs attributes");
650
651 scale3d.init = 1;
652 }
653
654 nvhost_scale3d_reset();
655}
656
657void nvhost_scale3d_deinit(struct nvhost_device *dev)
658{
659 device_remove_file(&dev->dev, &dev_attr_enable_3d_scaling);
660 scale3d.init = 0;
661}
diff --git a/drivers/video/tegra/host/gr3d/scale3d.h b/drivers/video/tegra/host/gr3d/scale3d.h
new file mode 100644
index 00000000000..f8aae1d591a
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/scale3d.h
@@ -0,0 +1,47 @@
1/*
2 * drivers/video/tegra/host/t30/scale3d.h
3 *
4 * Tegra Graphics Host 3D Clock Scaling
5 *
6 * Copyright (c) 2010-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#ifndef NVHOST_T30_SCALE3D_H
22#define NVHOST_T30_SCALE3D_H
23
24struct nvhost_device;
25struct device;
26struct dentry;
27
28/* Initialization and de-initialization for module */
29void nvhost_scale3d_init(struct nvhost_device *);
30void nvhost_scale3d_deinit(struct nvhost_device *);
31
32/* Suspend is called when powering down module */
33void nvhost_scale3d_suspend(struct nvhost_device *);
34
35/* reset 3d module load counters, called on resume */
36void nvhost_scale3d_reset(void);
37
38/*
39 * call when performing submit to notify scaling mechanism that 3d module is
40 * in use
41 */
42void nvhost_scale3d_notify_busy(struct nvhost_device *);
43void nvhost_scale3d_notify_idle(struct nvhost_device *);
44
45void nvhost_scale3d_debug_init(struct dentry *de);
46
47#endif