]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5f60ed0d TR |
2 | /* |
3 | * Copyright (C) 2013 Avionic Design GmbH | |
4 | * Copyright (C) 2013 NVIDIA Corporation | |
5f60ed0d TR |
5 | */ |
6 | ||
7 | #include <linux/clk.h> | |
8 | #include <linux/host1x.h> | |
c9ac5217 | 9 | #include <linux/iommu.h> |
5f60ed0d | 10 | #include <linux/module.h> |
33f150ea | 11 | #include <linux/of_device.h> |
5f60ed0d | 12 | #include <linux/platform_device.h> |
ca48080a | 13 | #include <linux/reset.h> |
306a7f91 | 14 | |
7232398a | 15 | #include <soc/tegra/pmc.h> |
5f60ed0d TR |
16 | |
17 | #include "drm.h" | |
18 | #include "gem.h" | |
19 | #include "gr3d.h" | |
20 | ||
33f150ea TR |
21 | struct gr3d_soc { |
22 | unsigned int version; | |
23 | }; | |
24 | ||
5f60ed0d | 25 | struct gr3d { |
c9ac5217 | 26 | struct iommu_group *group; |
5f60ed0d TR |
27 | struct tegra_drm_client client; |
28 | struct host1x_channel *channel; | |
29 | struct clk *clk_secondary; | |
30 | struct clk *clk; | |
ca48080a SW |
31 | struct reset_control *rst_secondary; |
32 | struct reset_control *rst; | |
5f60ed0d | 33 | |
33f150ea TR |
34 | const struct gr3d_soc *soc; |
35 | ||
5f60ed0d TR |
36 | DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS); |
37 | }; | |
38 | ||
39 | static inline struct gr3d *to_gr3d(struct tegra_drm_client *client) | |
40 | { | |
41 | return container_of(client, struct gr3d, client); | |
42 | } | |
43 | ||
44 | static int gr3d_init(struct host1x_client *client) | |
45 | { | |
46 | struct tegra_drm_client *drm = host1x_to_drm_client(client); | |
9910f5c4 | 47 | struct drm_device *dev = dev_get_drvdata(client->parent); |
977386a0 | 48 | unsigned long flags = HOST1X_SYNCPT_HAS_BASE; |
5f60ed0d | 49 | struct gr3d *gr3d = to_gr3d(drm); |
c9ac5217 | 50 | int err; |
5f60ed0d TR |
51 | |
52 | gr3d->channel = host1x_channel_request(client->dev); | |
53 | if (!gr3d->channel) | |
54 | return -ENOMEM; | |
55 | ||
617dd7cc | 56 | client->syncpts[0] = host1x_syncpt_request(client, flags); |
5f60ed0d | 57 | if (!client->syncpts[0]) { |
230630bd TR |
58 | err = -ENOMEM; |
59 | dev_err(client->dev, "failed to request syncpoint: %d\n", err); | |
60 | goto put; | |
5f60ed0d TR |
61 | } |
62 | ||
0c407de5 TR |
63 | gr3d->group = host1x_client_iommu_attach(client, false); |
64 | if (IS_ERR(gr3d->group)) { | |
65 | err = PTR_ERR(gr3d->group); | |
66 | dev_err(client->dev, "failed to attach to domain: %d\n", err); | |
67 | goto free; | |
c9ac5217 DO |
68 | } |
69 | ||
230630bd TR |
70 | err = tegra_drm_register_client(dev->dev_private, drm); |
71 | if (err < 0) { | |
72 | dev_err(client->dev, "failed to register client: %d\n", err); | |
73 | goto detach; | |
74 | } | |
75 | ||
76 | return 0; | |
77 | ||
78 | detach: | |
0c407de5 | 79 | host1x_client_iommu_detach(client, gr3d->group); |
230630bd TR |
80 | free: |
81 | host1x_syncpt_free(client->syncpts[0]); | |
82 | put: | |
83 | host1x_channel_put(gr3d->channel); | |
84 | return err; | |
5f60ed0d TR |
85 | } |
86 | ||
87 | static int gr3d_exit(struct host1x_client *client) | |
88 | { | |
89 | struct tegra_drm_client *drm = host1x_to_drm_client(client); | |
9910f5c4 | 90 | struct drm_device *dev = dev_get_drvdata(client->parent); |
5f60ed0d TR |
91 | struct gr3d *gr3d = to_gr3d(drm); |
92 | int err; | |
93 | ||
9910f5c4 | 94 | err = tegra_drm_unregister_client(dev->dev_private, drm); |
5f60ed0d TR |
95 | if (err < 0) |
96 | return err; | |
97 | ||
0c407de5 | 98 | host1x_client_iommu_detach(client, gr3d->group); |
5f60ed0d | 99 | host1x_syncpt_free(client->syncpts[0]); |
8474b025 | 100 | host1x_channel_put(gr3d->channel); |
5f60ed0d TR |
101 | |
102 | return 0; | |
103 | } | |
104 | ||
105 | static const struct host1x_client_ops gr3d_client_ops = { | |
106 | .init = gr3d_init, | |
107 | .exit = gr3d_exit, | |
108 | }; | |
109 | ||
110 | static int gr3d_open_channel(struct tegra_drm_client *client, | |
111 | struct tegra_drm_context *context) | |
112 | { | |
113 | struct gr3d *gr3d = to_gr3d(client); | |
114 | ||
115 | context->channel = host1x_channel_get(gr3d->channel); | |
116 | if (!context->channel) | |
117 | return -ENOMEM; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | static void gr3d_close_channel(struct tegra_drm_context *context) | |
123 | { | |
124 | host1x_channel_put(context->channel); | |
125 | } | |
126 | ||
127 | static int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset) | |
128 | { | |
129 | struct gr3d *gr3d = dev_get_drvdata(dev); | |
130 | ||
131 | switch (class) { | |
132 | case HOST1X_CLASS_HOST1X: | |
133 | if (offset == 0x2b) | |
134 | return 1; | |
135 | ||
136 | break; | |
137 | ||
138 | case HOST1X_CLASS_GR3D: | |
139 | if (offset >= GR3D_NUM_REGS) | |
140 | break; | |
141 | ||
142 | if (test_bit(offset, gr3d->addr_regs)) | |
143 | return 1; | |
144 | ||
145 | break; | |
146 | } | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | static const struct tegra_drm_client_ops gr3d_ops = { | |
152 | .open_channel = gr3d_open_channel, | |
153 | .close_channel = gr3d_close_channel, | |
154 | .is_addr_reg = gr3d_is_addr_reg, | |
155 | .submit = tegra_drm_submit, | |
156 | }; | |
157 | ||
33f150ea TR |
158 | static const struct gr3d_soc tegra20_gr3d_soc = { |
159 | .version = 0x20, | |
160 | }; | |
161 | ||
162 | static const struct gr3d_soc tegra30_gr3d_soc = { | |
163 | .version = 0x30, | |
164 | }; | |
165 | ||
166 | static const struct gr3d_soc tegra114_gr3d_soc = { | |
167 | .version = 0x35, | |
168 | }; | |
169 | ||
5f60ed0d | 170 | static const struct of_device_id tegra_gr3d_match[] = { |
33f150ea TR |
171 | { .compatible = "nvidia,tegra114-gr3d", .data = &tegra114_gr3d_soc }, |
172 | { .compatible = "nvidia,tegra30-gr3d", .data = &tegra30_gr3d_soc }, | |
173 | { .compatible = "nvidia,tegra20-gr3d", .data = &tegra20_gr3d_soc }, | |
5f60ed0d TR |
174 | { } |
175 | }; | |
ef70728c | 176 | MODULE_DEVICE_TABLE(of, tegra_gr3d_match); |
5f60ed0d TR |
177 | |
178 | static const u32 gr3d_addr_regs[] = { | |
179 | GR3D_IDX_ATTRIBUTE( 0), | |
180 | GR3D_IDX_ATTRIBUTE( 1), | |
181 | GR3D_IDX_ATTRIBUTE( 2), | |
182 | GR3D_IDX_ATTRIBUTE( 3), | |
183 | GR3D_IDX_ATTRIBUTE( 4), | |
184 | GR3D_IDX_ATTRIBUTE( 5), | |
185 | GR3D_IDX_ATTRIBUTE( 6), | |
186 | GR3D_IDX_ATTRIBUTE( 7), | |
187 | GR3D_IDX_ATTRIBUTE( 8), | |
188 | GR3D_IDX_ATTRIBUTE( 9), | |
189 | GR3D_IDX_ATTRIBUTE(10), | |
190 | GR3D_IDX_ATTRIBUTE(11), | |
191 | GR3D_IDX_ATTRIBUTE(12), | |
192 | GR3D_IDX_ATTRIBUTE(13), | |
193 | GR3D_IDX_ATTRIBUTE(14), | |
194 | GR3D_IDX_ATTRIBUTE(15), | |
195 | GR3D_IDX_INDEX_BASE, | |
196 | GR3D_QR_ZTAG_ADDR, | |
197 | GR3D_QR_CTAG_ADDR, | |
198 | GR3D_QR_CZ_ADDR, | |
199 | GR3D_TEX_TEX_ADDR( 0), | |
200 | GR3D_TEX_TEX_ADDR( 1), | |
201 | GR3D_TEX_TEX_ADDR( 2), | |
202 | GR3D_TEX_TEX_ADDR( 3), | |
203 | GR3D_TEX_TEX_ADDR( 4), | |
204 | GR3D_TEX_TEX_ADDR( 5), | |
205 | GR3D_TEX_TEX_ADDR( 6), | |
206 | GR3D_TEX_TEX_ADDR( 7), | |
207 | GR3D_TEX_TEX_ADDR( 8), | |
208 | GR3D_TEX_TEX_ADDR( 9), | |
209 | GR3D_TEX_TEX_ADDR(10), | |
210 | GR3D_TEX_TEX_ADDR(11), | |
211 | GR3D_TEX_TEX_ADDR(12), | |
212 | GR3D_TEX_TEX_ADDR(13), | |
213 | GR3D_TEX_TEX_ADDR(14), | |
214 | GR3D_TEX_TEX_ADDR(15), | |
215 | GR3D_DW_MEMORY_OUTPUT_ADDRESS, | |
216 | GR3D_GLOBAL_SURFADDR( 0), | |
217 | GR3D_GLOBAL_SURFADDR( 1), | |
218 | GR3D_GLOBAL_SURFADDR( 2), | |
219 | GR3D_GLOBAL_SURFADDR( 3), | |
220 | GR3D_GLOBAL_SURFADDR( 4), | |
221 | GR3D_GLOBAL_SURFADDR( 5), | |
222 | GR3D_GLOBAL_SURFADDR( 6), | |
223 | GR3D_GLOBAL_SURFADDR( 7), | |
224 | GR3D_GLOBAL_SURFADDR( 8), | |
225 | GR3D_GLOBAL_SURFADDR( 9), | |
226 | GR3D_GLOBAL_SURFADDR(10), | |
227 | GR3D_GLOBAL_SURFADDR(11), | |
228 | GR3D_GLOBAL_SURFADDR(12), | |
229 | GR3D_GLOBAL_SURFADDR(13), | |
230 | GR3D_GLOBAL_SURFADDR(14), | |
231 | GR3D_GLOBAL_SURFADDR(15), | |
232 | GR3D_GLOBAL_SPILLSURFADDR, | |
233 | GR3D_GLOBAL_SURFOVERADDR( 0), | |
234 | GR3D_GLOBAL_SURFOVERADDR( 1), | |
235 | GR3D_GLOBAL_SURFOVERADDR( 2), | |
236 | GR3D_GLOBAL_SURFOVERADDR( 3), | |
237 | GR3D_GLOBAL_SURFOVERADDR( 4), | |
238 | GR3D_GLOBAL_SURFOVERADDR( 5), | |
239 | GR3D_GLOBAL_SURFOVERADDR( 6), | |
240 | GR3D_GLOBAL_SURFOVERADDR( 7), | |
241 | GR3D_GLOBAL_SURFOVERADDR( 8), | |
242 | GR3D_GLOBAL_SURFOVERADDR( 9), | |
243 | GR3D_GLOBAL_SURFOVERADDR(10), | |
244 | GR3D_GLOBAL_SURFOVERADDR(11), | |
245 | GR3D_GLOBAL_SURFOVERADDR(12), | |
246 | GR3D_GLOBAL_SURFOVERADDR(13), | |
247 | GR3D_GLOBAL_SURFOVERADDR(14), | |
248 | GR3D_GLOBAL_SURFOVERADDR(15), | |
249 | GR3D_GLOBAL_SAMP01SURFADDR( 0), | |
250 | GR3D_GLOBAL_SAMP01SURFADDR( 1), | |
251 | GR3D_GLOBAL_SAMP01SURFADDR( 2), | |
252 | GR3D_GLOBAL_SAMP01SURFADDR( 3), | |
253 | GR3D_GLOBAL_SAMP01SURFADDR( 4), | |
254 | GR3D_GLOBAL_SAMP01SURFADDR( 5), | |
255 | GR3D_GLOBAL_SAMP01SURFADDR( 6), | |
256 | GR3D_GLOBAL_SAMP01SURFADDR( 7), | |
257 | GR3D_GLOBAL_SAMP01SURFADDR( 8), | |
258 | GR3D_GLOBAL_SAMP01SURFADDR( 9), | |
259 | GR3D_GLOBAL_SAMP01SURFADDR(10), | |
260 | GR3D_GLOBAL_SAMP01SURFADDR(11), | |
261 | GR3D_GLOBAL_SAMP01SURFADDR(12), | |
262 | GR3D_GLOBAL_SAMP01SURFADDR(13), | |
263 | GR3D_GLOBAL_SAMP01SURFADDR(14), | |
264 | GR3D_GLOBAL_SAMP01SURFADDR(15), | |
265 | GR3D_GLOBAL_SAMP23SURFADDR( 0), | |
266 | GR3D_GLOBAL_SAMP23SURFADDR( 1), | |
267 | GR3D_GLOBAL_SAMP23SURFADDR( 2), | |
268 | GR3D_GLOBAL_SAMP23SURFADDR( 3), | |
269 | GR3D_GLOBAL_SAMP23SURFADDR( 4), | |
270 | GR3D_GLOBAL_SAMP23SURFADDR( 5), | |
271 | GR3D_GLOBAL_SAMP23SURFADDR( 6), | |
272 | GR3D_GLOBAL_SAMP23SURFADDR( 7), | |
273 | GR3D_GLOBAL_SAMP23SURFADDR( 8), | |
274 | GR3D_GLOBAL_SAMP23SURFADDR( 9), | |
275 | GR3D_GLOBAL_SAMP23SURFADDR(10), | |
276 | GR3D_GLOBAL_SAMP23SURFADDR(11), | |
277 | GR3D_GLOBAL_SAMP23SURFADDR(12), | |
278 | GR3D_GLOBAL_SAMP23SURFADDR(13), | |
279 | GR3D_GLOBAL_SAMP23SURFADDR(14), | |
280 | GR3D_GLOBAL_SAMP23SURFADDR(15), | |
281 | }; | |
282 | ||
283 | static int gr3d_probe(struct platform_device *pdev) | |
284 | { | |
285 | struct device_node *np = pdev->dev.of_node; | |
286 | struct host1x_syncpt **syncpts; | |
287 | struct gr3d *gr3d; | |
288 | unsigned int i; | |
289 | int err; | |
290 | ||
291 | gr3d = devm_kzalloc(&pdev->dev, sizeof(*gr3d), GFP_KERNEL); | |
292 | if (!gr3d) | |
293 | return -ENOMEM; | |
294 | ||
33f150ea TR |
295 | gr3d->soc = of_device_get_match_data(&pdev->dev); |
296 | ||
5f60ed0d TR |
297 | syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL); |
298 | if (!syncpts) | |
299 | return -ENOMEM; | |
300 | ||
301 | gr3d->clk = devm_clk_get(&pdev->dev, NULL); | |
302 | if (IS_ERR(gr3d->clk)) { | |
303 | dev_err(&pdev->dev, "cannot get clock\n"); | |
304 | return PTR_ERR(gr3d->clk); | |
305 | } | |
306 | ||
ca48080a SW |
307 | gr3d->rst = devm_reset_control_get(&pdev->dev, "3d"); |
308 | if (IS_ERR(gr3d->rst)) { | |
309 | dev_err(&pdev->dev, "cannot get reset\n"); | |
310 | return PTR_ERR(gr3d->rst); | |
311 | } | |
312 | ||
5f60ed0d TR |
313 | if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) { |
314 | gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2"); | |
87ba3e15 | 315 | if (IS_ERR(gr3d->clk_secondary)) { |
5f60ed0d | 316 | dev_err(&pdev->dev, "cannot get secondary clock\n"); |
87ba3e15 | 317 | return PTR_ERR(gr3d->clk_secondary); |
5f60ed0d | 318 | } |
ca48080a SW |
319 | |
320 | gr3d->rst_secondary = devm_reset_control_get(&pdev->dev, | |
321 | "3d2"); | |
322 | if (IS_ERR(gr3d->rst_secondary)) { | |
323 | dev_err(&pdev->dev, "cannot get secondary reset\n"); | |
324 | return PTR_ERR(gr3d->rst_secondary); | |
325 | } | |
5f60ed0d TR |
326 | } |
327 | ||
80b28791 SW |
328 | err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk, |
329 | gr3d->rst); | |
5f60ed0d TR |
330 | if (err < 0) { |
331 | dev_err(&pdev->dev, "failed to power up 3D unit\n"); | |
332 | return err; | |
333 | } | |
334 | ||
335 | if (gr3d->clk_secondary) { | |
336 | err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1, | |
80b28791 SW |
337 | gr3d->clk_secondary, |
338 | gr3d->rst_secondary); | |
5f60ed0d TR |
339 | if (err < 0) { |
340 | dev_err(&pdev->dev, | |
341 | "failed to power up secondary 3D unit\n"); | |
342 | return err; | |
343 | } | |
344 | } | |
345 | ||
346 | INIT_LIST_HEAD(&gr3d->client.base.list); | |
347 | gr3d->client.base.ops = &gr3d_client_ops; | |
348 | gr3d->client.base.dev = &pdev->dev; | |
349 | gr3d->client.base.class = HOST1X_CLASS_GR3D; | |
350 | gr3d->client.base.syncpts = syncpts; | |
351 | gr3d->client.base.num_syncpts = 1; | |
352 | ||
353 | INIT_LIST_HEAD(&gr3d->client.list); | |
33f150ea | 354 | gr3d->client.version = gr3d->soc->version; |
5f60ed0d TR |
355 | gr3d->client.ops = &gr3d_ops; |
356 | ||
357 | err = host1x_client_register(&gr3d->client.base); | |
358 | if (err < 0) { | |
359 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | |
360 | err); | |
361 | return err; | |
362 | } | |
363 | ||
364 | /* initialize address register map */ | |
365 | for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) | |
366 | set_bit(gr3d_addr_regs[i], gr3d->addr_regs); | |
367 | ||
368 | platform_set_drvdata(pdev, gr3d); | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | static int gr3d_remove(struct platform_device *pdev) | |
374 | { | |
375 | struct gr3d *gr3d = platform_get_drvdata(pdev); | |
376 | int err; | |
377 | ||
378 | err = host1x_client_unregister(&gr3d->client.base); | |
379 | if (err < 0) { | |
380 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | |
381 | err); | |
382 | return err; | |
383 | } | |
384 | ||
385 | if (gr3d->clk_secondary) { | |
386 | tegra_powergate_power_off(TEGRA_POWERGATE_3D1); | |
387 | clk_disable_unprepare(gr3d->clk_secondary); | |
388 | } | |
389 | ||
390 | tegra_powergate_power_off(TEGRA_POWERGATE_3D); | |
391 | clk_disable_unprepare(gr3d->clk); | |
392 | ||
393 | return 0; | |
394 | } | |
395 | ||
396 | struct platform_driver tegra_gr3d_driver = { | |
397 | .driver = { | |
398 | .name = "tegra-gr3d", | |
399 | .of_match_table = tegra_gr3d_match, | |
400 | }, | |
401 | .probe = gr3d_probe, | |
402 | .remove = gr3d_remove, | |
403 | }; |