diff options
-rw-r--r-- | drivers/gpu/drm/nouveau/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/core/falcon.c | 247 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/core/falcon.h | 80 |
3 files changed, 328 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index a96af782eefa..af6c5c236ecb 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile | |||
@@ -11,6 +11,7 @@ nouveau-y := core/core/client.o | |||
11 | nouveau-y += core/core/engctx.o | 11 | nouveau-y += core/core/engctx.o |
12 | nouveau-y += core/core/engine.o | 12 | nouveau-y += core/core/engine.o |
13 | nouveau-y += core/core/enum.o | 13 | nouveau-y += core/core/enum.o |
14 | nouveau-y += core/core/falcon.o | ||
14 | nouveau-y += core/core/gpuobj.o | 15 | nouveau-y += core/core/gpuobj.o |
15 | nouveau-y += core/core/handle.o | 16 | nouveau-y += core/core/handle.o |
16 | nouveau-y += core/core/mm.o | 17 | nouveau-y += core/core/mm.o |
diff --git a/drivers/gpu/drm/nouveau/core/core/falcon.c b/drivers/gpu/drm/nouveau/core/core/falcon.c new file mode 100644 index 000000000000..6b0843c33877 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/falcon.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * Copyright 2012 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 | |||
23 | #include <core/falcon.h> | ||
24 | |||
25 | #include <subdev/timer.h> | ||
26 | |||
27 | u32 | ||
28 | _nouveau_falcon_rd32(struct nouveau_object *object, u64 addr) | ||
29 | { | ||
30 | struct nouveau_falcon *falcon = (void *)object; | ||
31 | return nv_rd32(falcon, falcon->addr + addr); | ||
32 | } | ||
33 | |||
34 | void | ||
35 | _nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data) | ||
36 | { | ||
37 | struct nouveau_falcon *falcon = (void *)object; | ||
38 | nv_wr32(falcon, falcon->addr + addr, data); | ||
39 | } | ||
40 | |||
41 | int | ||
42 | _nouveau_falcon_init(struct nouveau_object *object) | ||
43 | { | ||
44 | struct nouveau_device *device = nv_device(object); | ||
45 | struct nouveau_falcon *falcon = (void *)object; | ||
46 | const struct firmware *fw; | ||
47 | char name[32] = "internal"; | ||
48 | int ret, i; | ||
49 | u32 caps; | ||
50 | |||
51 | /* enable engine, and determine its capabilities */ | ||
52 | ret = nouveau_engine_init(&falcon->base); | ||
53 | if (ret) | ||
54 | return ret; | ||
55 | |||
56 | if (device->chipset < 0xa3 || | ||
57 | device->chipset == 0xaa || device->chipset == 0xac) { | ||
58 | falcon->version = 0; | ||
59 | falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; | ||
60 | } else { | ||
61 | caps = nv_ro32(falcon, 0x12c); | ||
62 | falcon->version = (caps & 0x0000000f); | ||
63 | falcon->secret = (caps & 0x00000030) >> 4; | ||
64 | } | ||
65 | |||
66 | caps = nv_ro32(falcon, 0x108); | ||
67 | falcon->code.limit = (caps & 0x000001ff) << 8; | ||
68 | falcon->data.limit = (caps & 0x0003fe00) >> 1; | ||
69 | |||
70 | nv_debug(falcon, "falcon version: %d\n", falcon->version); | ||
71 | nv_debug(falcon, "secret level: %d\n", falcon->secret); | ||
72 | nv_debug(falcon, "code limit: %d\n", falcon->code.limit); | ||
73 | nv_debug(falcon, "data limit: %d\n", falcon->data.limit); | ||
74 | |||
75 | /* wait for 'uc halted' to be signalled before continuing */ | ||
76 | if (falcon->secret) { | ||
77 | nv_wait(falcon, 0x008, 0x00000010, 0x00000010); | ||
78 | nv_wo32(falcon, 0x004, 0x00000010); | ||
79 | } | ||
80 | |||
81 | /* disable all interrupts */ | ||
82 | nv_wo32(falcon, 0x014, 0xffffffff); | ||
83 | |||
84 | /* no default ucode provided by the engine implementation, try and | ||
85 | * locate a "self-bootstrapping" firmware image for the engine | ||
86 | */ | ||
87 | if (!falcon->code.data) { | ||
88 | snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", | ||
89 | device->chipset, falcon->addr >> 12); | ||
90 | |||
91 | ret = request_firmware(&fw, name, &device->pdev->dev); | ||
92 | if (ret == 0) { | ||
93 | falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); | ||
94 | falcon->code.size = fw->size; | ||
95 | falcon->data.data = NULL; | ||
96 | falcon->data.size = 0; | ||
97 | release_firmware(fw); | ||
98 | } | ||
99 | |||
100 | falcon->external = true; | ||
101 | } | ||
102 | |||
103 | /* next step is to try and load "static code/data segment" firmware | ||
104 | * images for the engine | ||
105 | */ | ||
106 | if (!falcon->code.data) { | ||
107 | snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", | ||
108 | device->chipset, falcon->addr >> 12); | ||
109 | |||
110 | ret = request_firmware(&fw, name, &device->pdev->dev); | ||
111 | if (ret) { | ||
112 | nv_error(falcon, "unable to load firmware data\n"); | ||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL); | ||
117 | falcon->data.size = fw->size; | ||
118 | release_firmware(fw); | ||
119 | if (!falcon->data.data) | ||
120 | return -ENOMEM; | ||
121 | |||
122 | snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", | ||
123 | device->chipset, falcon->addr >> 12); | ||
124 | |||
125 | ret = request_firmware(&fw, name, &device->pdev->dev); | ||
126 | if (ret) { | ||
127 | nv_error(falcon, "unable to load firmware code\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); | ||
132 | falcon->code.size = fw->size; | ||
133 | release_firmware(fw); | ||
134 | if (!falcon->code.data) | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ? | ||
139 | "static code/data segments" : "self-bootstrapping"); | ||
140 | |||
141 | /* ensure any "self-bootstrapping" firmware image is in vram */ | ||
142 | if (!falcon->data.data && !falcon->core) { | ||
143 | ret = nouveau_gpuobj_new(object->parent, NULL, | ||
144 | falcon->code.size, 256, 0, | ||
145 | &falcon->core); | ||
146 | if (ret) { | ||
147 | nv_error(falcon, "core allocation failed, %d\n", ret); | ||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | for (i = 0; i < falcon->code.size; i += 4) | ||
152 | nv_wo32(falcon->core, i, falcon->code.data[i / 4]); | ||
153 | } | ||
154 | |||
155 | /* upload firmware bootloader (or the full code segments) */ | ||
156 | if (falcon->core) { | ||
157 | if (device->card_type < NV_C0) | ||
158 | nv_wo32(falcon, 0x618, 0x04000000); | ||
159 | else | ||
160 | nv_wo32(falcon, 0x618, 0x00000114); | ||
161 | nv_wo32(falcon, 0x11c, 0); | ||
162 | nv_wo32(falcon, 0x110, falcon->core->addr >> 8); | ||
163 | nv_wo32(falcon, 0x114, 0); | ||
164 | nv_wo32(falcon, 0x118, 0x00006610); | ||
165 | } else { | ||
166 | if (falcon->code.size > falcon->code.limit || | ||
167 | falcon->data.size > falcon->data.limit) { | ||
168 | nv_error(falcon, "ucode exceeds falcon limit(s)\n"); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | if (falcon->version < 3) { | ||
173 | nv_wo32(falcon, 0xff8, 0x00100000); | ||
174 | for (i = 0; i < falcon->code.size / 4; i++) | ||
175 | nv_wo32(falcon, 0xff4, falcon->code.data[i]); | ||
176 | } else { | ||
177 | nv_wo32(falcon, 0x180, 0x01000000); | ||
178 | for (i = 0; i < falcon->code.size / 4; i++) { | ||
179 | if ((i & 0x3f) == 0) | ||
180 | nv_wo32(falcon, 0x188, i >> 6); | ||
181 | nv_wo32(falcon, 0x184, falcon->code.data[i]); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /* upload data segment (if necessary), zeroing the remainder */ | ||
187 | if (falcon->version < 3) { | ||
188 | nv_wo32(falcon, 0xff8, 0x00000000); | ||
189 | for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) | ||
190 | nv_wo32(falcon, 0xff4, falcon->data.data[i]); | ||
191 | for (; i < falcon->data.limit; i += 4) | ||
192 | nv_wo32(falcon, 0xff4, 0x00000000); | ||
193 | } else { | ||
194 | nv_wo32(falcon, 0x1c0, 0x01000000); | ||
195 | for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) | ||
196 | nv_wo32(falcon, 0x1c4, falcon->data.data[i]); | ||
197 | for (; i < falcon->data.limit / 4; i++) | ||
198 | nv_wo32(falcon, 0x1c4, 0x00000000); | ||
199 | } | ||
200 | |||
201 | /* start it running */ | ||
202 | nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ | ||
203 | nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */ | ||
204 | nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */ | ||
205 | nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */ | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | int | ||
210 | _nouveau_falcon_fini(struct nouveau_object *object, bool suspend) | ||
211 | { | ||
212 | struct nouveau_falcon *falcon = (void *)object; | ||
213 | |||
214 | if (!suspend) { | ||
215 | nouveau_gpuobj_ref(NULL, &falcon->core); | ||
216 | if (falcon->external) { | ||
217 | kfree(falcon->data.data); | ||
218 | kfree(falcon->code.data); | ||
219 | falcon->code.data = NULL; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | nv_mo32(falcon, 0x048, 0x00000003, 0x00000000); | ||
224 | nv_wo32(falcon, 0x014, 0xffffffff); | ||
225 | |||
226 | return nouveau_engine_fini(&falcon->base, suspend); | ||
227 | } | ||
228 | |||
229 | int | ||
230 | nouveau_falcon_create_(struct nouveau_object *parent, | ||
231 | struct nouveau_object *engine, | ||
232 | struct nouveau_oclass *oclass, u32 addr, bool enable, | ||
233 | const char *iname, const char *fname, | ||
234 | int length, void **pobject) | ||
235 | { | ||
236 | struct nouveau_falcon *falcon; | ||
237 | int ret; | ||
238 | |||
239 | ret = nouveau_engine_create_(parent, engine, oclass, enable, iname, | ||
240 | fname, length, pobject); | ||
241 | falcon = *pobject; | ||
242 | if (ret) | ||
243 | return ret; | ||
244 | |||
245 | falcon->addr = addr; | ||
246 | return 0; | ||
247 | } | ||
diff --git a/drivers/gpu/drm/nouveau/core/include/core/falcon.h b/drivers/gpu/drm/nouveau/core/include/core/falcon.h new file mode 100644 index 000000000000..47f040ae49f1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/falcon.h | |||
@@ -0,0 +1,80 @@ | |||
1 | #ifndef __NOUVEAU_FALCON_H__ | ||
2 | #define __NOUVEAU_FALCON_H__ | ||
3 | |||
4 | #include <core/engine.h> | ||
5 | #include <core/engctx.h> | ||
6 | #include <core/gpuobj.h> | ||
7 | |||
8 | struct nouveau_falcon_chan { | ||
9 | struct nouveau_engctx base; | ||
10 | }; | ||
11 | |||
12 | #define nouveau_falcon_context_create(p,e,c,g,s,a,f,d) \ | ||
13 | nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) | ||
14 | #define nouveau_falcon_context_destroy(d) \ | ||
15 | nouveau_engctx_destroy(&(d)->base) | ||
16 | #define nouveau_falcon_context_init(d) \ | ||
17 | nouveau_engctx_init(&(d)->base) | ||
18 | #define nouveau_falcon_context_fini(d,s) \ | ||
19 | nouveau_engctx_fini(&(d)->base, (s)) | ||
20 | |||
21 | #define _nouveau_falcon_context_dtor _nouveau_engctx_dtor | ||
22 | #define _nouveau_falcon_context_init _nouveau_engctx_init | ||
23 | #define _nouveau_falcon_context_fini _nouveau_engctx_fini | ||
24 | #define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32 | ||
25 | #define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32 | ||
26 | |||
27 | struct nouveau_falcon_data { | ||
28 | bool external; | ||
29 | }; | ||
30 | |||
31 | struct nouveau_falcon { | ||
32 | struct nouveau_engine base; | ||
33 | |||
34 | u32 addr; | ||
35 | u8 version; | ||
36 | u8 secret; | ||
37 | |||
38 | struct nouveau_gpuobj *core; | ||
39 | bool external; | ||
40 | |||
41 | struct { | ||
42 | u32 limit; | ||
43 | u32 *data; | ||
44 | u32 size; | ||
45 | } code; | ||
46 | |||
47 | struct { | ||
48 | u32 limit; | ||
49 | u32 *data; | ||
50 | u32 size; | ||
51 | } data; | ||
52 | }; | ||
53 | |||
54 | #define nv_falcon(priv) (&(priv)->base) | ||
55 | |||
56 | #define nouveau_falcon_create(p,e,c,b,d,i,f,r) \ | ||
57 | nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f), \ | ||
58 | sizeof(**r),(void **)r) | ||
59 | #define nouveau_falcon_destroy(p) \ | ||
60 | nouveau_engine_destroy(&(p)->base) | ||
61 | #define nouveau_falcon_init(p) ({ \ | ||
62 | struct nouveau_falcon *falcon = (p); \ | ||
63 | _nouveau_falcon_init(nv_object(falcon)); \ | ||
64 | }) | ||
65 | #define nouveau_falcon_fini(p,s) ({ \ | ||
66 | struct nouveau_falcon *falcon = (p); \ | ||
67 | _nouveau_falcon_fini(nv_object(falcon), (s)); \ | ||
68 | }) | ||
69 | |||
70 | int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *, | ||
71 | struct nouveau_oclass *, u32, bool, const char *, | ||
72 | const char *, int, void **); | ||
73 | |||
74 | #define _nouveau_falcon_dtor _nouveau_engine_dtor | ||
75 | int _nouveau_falcon_init(struct nouveau_object *); | ||
76 | int _nouveau_falcon_fini(struct nouveau_object *, bool); | ||
77 | u32 _nouveau_falcon_rd32(struct nouveau_object *, u64); | ||
78 | void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32); | ||
79 | |||
80 | #endif | ||