]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
bfd28b13 RC |
2 | /* |
3 | * Copyright (C) 2013-2014 Red Hat | |
4 | * Author: Rob Clark <robdclark@gmail.com> | |
5 | * | |
bf5af4ae | 6 | * Copyright (c) 2014,2017 The Linux Foundation. All rights reserved. |
bfd28b13 RC |
7 | */ |
8 | ||
9 | #include "adreno_gpu.h" | |
10 | ||
e2550b7a RC |
11 | #define ANY_ID 0xff |
12 | ||
3526e9fb RC |
13 | bool hang_debug = false; |
14 | MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)"); | |
15 | module_param_named(hang_debug, hang_debug, bool, 0600); | |
16 | ||
e2550b7a RC |
17 | static const struct adreno_info gpulist[] = { |
18 | { | |
21af872c JM |
19 | .rev = ADRENO_REV(2, 0, 0, 0), |
20 | .revn = 200, | |
21 | .name = "A200", | |
22 | .fw = { | |
23 | [ADRENO_FW_PM4] = "yamato_pm4.fw", | |
24 | [ADRENO_FW_PFP] = "yamato_pfp.fw", | |
25 | }, | |
26 | .gmem = SZ_256K, | |
27 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, | |
28 | .init = a2xx_gpu_init, | |
29 | }, { /* a200 on i.mx51 has only 128kib gmem */ | |
30 | .rev = ADRENO_REV(2, 0, 0, 1), | |
31 | .revn = 201, | |
32 | .name = "A200", | |
33 | .fw = { | |
34 | [ADRENO_FW_PM4] = "yamato_pm4.fw", | |
35 | [ADRENO_FW_PFP] = "yamato_pfp.fw", | |
36 | }, | |
37 | .gmem = SZ_128K, | |
38 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, | |
39 | .init = a2xx_gpu_init, | |
40 | }, { | |
41 | .rev = ADRENO_REV(2, 2, 0, ANY_ID), | |
42 | .revn = 220, | |
43 | .name = "A220", | |
44 | .fw = { | |
45 | [ADRENO_FW_PM4] = "leia_pm4_470.fw", | |
46 | [ADRENO_FW_PFP] = "leia_pfp_470.fw", | |
47 | }, | |
48 | .gmem = SZ_512K, | |
49 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, | |
50 | .init = a2xx_gpu_init, | |
51 | }, { | |
e2550b7a RC |
52 | .rev = ADRENO_REV(3, 0, 5, ANY_ID), |
53 | .revn = 305, | |
54 | .name = "A305", | |
c5e3548c JC |
55 | .fw = { |
56 | [ADRENO_FW_PM4] = "a300_pm4.fw", | |
57 | [ADRENO_FW_PFP] = "a300_pfp.fw", | |
58 | }, | |
e2550b7a | 59 | .gmem = SZ_256K, |
64709686 | 60 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
e2550b7a | 61 | .init = a3xx_gpu_init, |
de558cd2 RC |
62 | }, { |
63 | .rev = ADRENO_REV(3, 0, 6, 0), | |
64 | .revn = 307, /* because a305c is revn==306 */ | |
65 | .name = "A306", | |
c5e3548c JC |
66 | .fw = { |
67 | [ADRENO_FW_PM4] = "a300_pm4.fw", | |
68 | [ADRENO_FW_PFP] = "a300_pfp.fw", | |
69 | }, | |
de558cd2 | 70 | .gmem = SZ_128K, |
64709686 | 71 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
de558cd2 | 72 | .init = a3xx_gpu_init, |
e2550b7a RC |
73 | }, { |
74 | .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID), | |
75 | .revn = 320, | |
76 | .name = "A320", | |
c5e3548c JC |
77 | .fw = { |
78 | [ADRENO_FW_PM4] = "a300_pm4.fw", | |
79 | [ADRENO_FW_PFP] = "a300_pfp.fw", | |
80 | }, | |
e2550b7a | 81 | .gmem = SZ_512K, |
64709686 | 82 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
e2550b7a RC |
83 | .init = a3xx_gpu_init, |
84 | }, { | |
85 | .rev = ADRENO_REV(3, 3, 0, ANY_ID), | |
86 | .revn = 330, | |
87 | .name = "A330", | |
c5e3548c JC |
88 | .fw = { |
89 | [ADRENO_FW_PM4] = "a330_pm4.fw", | |
90 | [ADRENO_FW_PFP] = "a330_pfp.fw", | |
91 | }, | |
e2550b7a | 92 | .gmem = SZ_1M, |
64709686 | 93 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
e2550b7a | 94 | .init = a3xx_gpu_init, |
23bd62fd AG |
95 | }, { |
96 | .rev = ADRENO_REV(4, 2, 0, ANY_ID), | |
97 | .revn = 420, | |
98 | .name = "A420", | |
c5e3548c JC |
99 | .fw = { |
100 | [ADRENO_FW_PM4] = "a420_pm4.fw", | |
101 | [ADRENO_FW_PFP] = "a420_pfp.fw", | |
102 | }, | |
23bd62fd | 103 | .gmem = (SZ_1M + SZ_512K), |
64709686 | 104 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
23bd62fd | 105 | .init = a4xx_gpu_init, |
357ff00b CS |
106 | }, { |
107 | .rev = ADRENO_REV(4, 3, 0, ANY_ID), | |
108 | .revn = 430, | |
109 | .name = "A430", | |
c5e3548c JC |
110 | .fw = { |
111 | [ADRENO_FW_PM4] = "a420_pm4.fw", | |
112 | [ADRENO_FW_PFP] = "a420_pfp.fw", | |
113 | }, | |
357ff00b | 114 | .gmem = (SZ_1M + SZ_512K), |
64709686 | 115 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
357ff00b | 116 | .init = a4xx_gpu_init, |
b5f103ab | 117 | }, { |
4e09b95d | 118 | .rev = ADRENO_REV(5, 3, 0, 2), |
b5f103ab JC |
119 | .revn = 530, |
120 | .name = "A530", | |
c5e3548c JC |
121 | .fw = { |
122 | [ADRENO_FW_PM4] = "a530_pm4.fw", | |
123 | [ADRENO_FW_PFP] = "a530_pfp.fw", | |
124 | [ADRENO_FW_GPMU] = "a530v3_gpmu.fw2", | |
125 | }, | |
b5f103ab | 126 | .gmem = SZ_1M, |
64709686 JC |
127 | /* |
128 | * Increase inactive period to 250 to avoid bouncing | |
129 | * the GDSC which appears to make it grumpy | |
130 | */ | |
131 | .inactive_period = 250, | |
4e09b95d RC |
132 | .quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI | |
133 | ADRENO_QUIRK_FAULT_DETECT_MASK, | |
b5f103ab | 134 | .init = a5xx_gpu_init, |
7c65817e | 135 | .zapfw = "a530_zap.mdt", |
4b565ca5 JC |
136 | }, { |
137 | .rev = ADRENO_REV(6, 3, 0, ANY_ID), | |
138 | .revn = 630, | |
139 | .name = "A630", | |
140 | .fw = { | |
141 | [ADRENO_FW_SQE] = "a630_sqe.fw", | |
142 | [ADRENO_FW_GMU] = "a630_gmu.bin", | |
143 | }, | |
144 | .gmem = SZ_1M, | |
bdacdcf2 | 145 | .inactive_period = DRM_MSM_INACTIVE_PERIOD, |
4b565ca5 | 146 | .init = a6xx_gpu_init, |
abccb9fe | 147 | .zapfw = "a630_zap.mdt", |
e2550b7a RC |
148 | }, |
149 | }; | |
150 | ||
73fc251f ND |
151 | MODULE_FIRMWARE("qcom/a300_pm4.fw"); |
152 | MODULE_FIRMWARE("qcom/a300_pfp.fw"); | |
153 | MODULE_FIRMWARE("qcom/a330_pm4.fw"); | |
154 | MODULE_FIRMWARE("qcom/a330_pfp.fw"); | |
155 | MODULE_FIRMWARE("qcom/a420_pm4.fw"); | |
156 | MODULE_FIRMWARE("qcom/a420_pfp.fw"); | |
ad40dfdc | 157 | MODULE_FIRMWARE("qcom/a530_pm4.fw"); |
73fc251f | 158 | MODULE_FIRMWARE("qcom/a530_pfp.fw"); |
ad40dfdc ND |
159 | MODULE_FIRMWARE("qcom/a530v3_gpmu.fw2"); |
160 | MODULE_FIRMWARE("qcom/a530_zap.mdt"); | |
161 | MODULE_FIRMWARE("qcom/a530_zap.b00"); | |
162 | MODULE_FIRMWARE("qcom/a530_zap.b01"); | |
163 | MODULE_FIRMWARE("qcom/a530_zap.b02"); | |
4b565ca5 JC |
164 | MODULE_FIRMWARE("qcom/a630_sqe.fw"); |
165 | MODULE_FIRMWARE("qcom/a630_gmu.bin"); | |
e2550b7a RC |
166 | |
167 | static inline bool _rev_match(uint8_t entry, uint8_t id) | |
168 | { | |
169 | return (entry == ANY_ID) || (entry == id); | |
170 | } | |
171 | ||
172 | const struct adreno_info *adreno_info(struct adreno_rev rev) | |
173 | { | |
174 | int i; | |
175 | ||
176 | /* identify gpu: */ | |
177 | for (i = 0; i < ARRAY_SIZE(gpulist); i++) { | |
178 | const struct adreno_info *info = &gpulist[i]; | |
179 | if (_rev_match(info->rev.core, rev.core) && | |
180 | _rev_match(info->rev.major, rev.major) && | |
181 | _rev_match(info->rev.minor, rev.minor) && | |
182 | _rev_match(info->rev.patchid, rev.patchid)) | |
183 | return info; | |
184 | } | |
185 | ||
186 | return NULL; | |
187 | } | |
188 | ||
189 | struct msm_gpu *adreno_load_gpu(struct drm_device *dev) | |
190 | { | |
191 | struct msm_drm_private *priv = dev->dev_private; | |
192 | struct platform_device *pdev = priv->gpu_pdev; | |
9dcfbc18 | 193 | struct msm_gpu *gpu = NULL; |
2c087a33 | 194 | struct adreno_gpu *adreno_gpu; |
eec874ce | 195 | int ret; |
e2550b7a | 196 | |
9dcfbc18 AT |
197 | if (pdev) |
198 | gpu = platform_get_drvdata(pdev); | |
199 | ||
eec874ce | 200 | if (!gpu) { |
9dcfbc18 | 201 | dev_err_once(dev->dev, "no GPU device was found\n"); |
e2550b7a RC |
202 | return NULL; |
203 | } | |
204 | ||
2c087a33 JC |
205 | adreno_gpu = to_adreno_gpu(gpu); |
206 | ||
207 | /* | |
208 | * The number one reason for HW init to fail is if the firmware isn't | |
209 | * loaded yet. Try that first and don't bother continuing on | |
210 | * otherwise | |
211 | */ | |
212 | ||
213 | ret = adreno_load_fw(adreno_gpu); | |
214 | if (ret) | |
215 | return NULL; | |
216 | ||
217 | /* Make sure pm runtime is active and reset any previous errors */ | |
218 | pm_runtime_set_active(&pdev->dev); | |
219 | ||
220 | ret = pm_runtime_get_sync(&pdev->dev); | |
221 | if (ret < 0) { | |
41570b74 | 222 | pm_runtime_put_sync(&pdev->dev); |
6a41da17 | 223 | DRM_DEV_ERROR(dev->dev, "Couldn't power up the GPU: %d\n", ret); |
2c087a33 JC |
224 | return NULL; |
225 | } | |
226 | ||
eec874ce RC |
227 | mutex_lock(&dev->struct_mutex); |
228 | ret = msm_gpu_hw_init(gpu); | |
229 | mutex_unlock(&dev->struct_mutex); | |
64709686 | 230 | pm_runtime_put_autosuspend(&pdev->dev); |
eec874ce | 231 | if (ret) { |
6a41da17 | 232 | DRM_DEV_ERROR(dev->dev, "gpu hw init failed: %d\n", ret); |
e2550b7a RC |
233 | return NULL; |
234 | } | |
235 | ||
288e5c88 | 236 | #ifdef CONFIG_DEBUG_FS |
331dc0bc RC |
237 | if (gpu->funcs->debugfs_init) { |
238 | gpu->funcs->debugfs_init(gpu, dev->primary); | |
239 | gpu->funcs->debugfs_init(gpu, dev->render); | |
331dc0bc | 240 | } |
288e5c88 | 241 | #endif |
331dc0bc | 242 | |
e2550b7a RC |
243 | return gpu; |
244 | } | |
245 | ||
bfd28b13 RC |
246 | static void set_gpu_pdev(struct drm_device *dev, |
247 | struct platform_device *pdev) | |
248 | { | |
249 | struct msm_drm_private *priv = dev->dev_private; | |
250 | priv->gpu_pdev = pdev; | |
251 | } | |
252 | ||
728bde66 | 253 | static int find_chipid(struct device *dev, struct adreno_rev *rev) |
1db7afa4 RC |
254 | { |
255 | struct device_node *node = dev->of_node; | |
256 | const char *compat; | |
257 | int ret; | |
728bde66 | 258 | u32 chipid; |
1db7afa4 RC |
259 | |
260 | /* first search the compat strings for qcom,adreno-XYZ.W: */ | |
261 | ret = of_property_read_string_index(node, "compatible", 0, &compat); | |
262 | if (ret == 0) { | |
728bde66 | 263 | unsigned int r, patch; |
1db7afa4 | 264 | |
e6f6d63e JM |
265 | if (sscanf(compat, "qcom,adreno-%u.%u", &r, &patch) == 2 || |
266 | sscanf(compat, "amd,imageon-%u.%u", &r, &patch) == 2) { | |
728bde66 JC |
267 | rev->core = r / 100; |
268 | r %= 100; | |
269 | rev->major = r / 10; | |
270 | r %= 10; | |
271 | rev->minor = r; | |
272 | rev->patchid = patch; | |
1db7afa4 RC |
273 | |
274 | return 0; | |
275 | } | |
276 | } | |
277 | ||
278 | /* and if that fails, fall back to legacy "qcom,chipid" property: */ | |
728bde66 JC |
279 | ret = of_property_read_u32(node, "qcom,chipid", &chipid); |
280 | if (ret) { | |
6a41da17 | 281 | DRM_DEV_ERROR(dev, "could not parse qcom,chipid: %d\n", ret); |
1db7afa4 | 282 | return ret; |
728bde66 JC |
283 | } |
284 | ||
285 | rev->core = (chipid >> 24) & 0xff; | |
286 | rev->major = (chipid >> 16) & 0xff; | |
287 | rev->minor = (chipid >> 8) & 0xff; | |
288 | rev->patchid = (chipid & 0xff); | |
1db7afa4 RC |
289 | |
290 | dev_warn(dev, "Using legacy qcom,chipid binding!\n"); | |
291 | dev_warn(dev, "Use compatible qcom,adreno-%u%u%u.%u instead.\n", | |
728bde66 | 292 | rev->core, rev->major, rev->minor, rev->patchid); |
1db7afa4 RC |
293 | |
294 | return 0; | |
295 | } | |
296 | ||
bfd28b13 RC |
297 | static int adreno_bind(struct device *dev, struct device *master, void *data) |
298 | { | |
299 | static struct adreno_platform_config config = {}; | |
eec874ce RC |
300 | const struct adreno_info *info; |
301 | struct drm_device *drm = dev_get_drvdata(master); | |
c2052a4e | 302 | struct msm_drm_private *priv = drm->dev_private; |
eec874ce | 303 | struct msm_gpu *gpu; |
4e09b95d | 304 | int ret; |
bfd28b13 | 305 | |
728bde66 JC |
306 | ret = find_chipid(dev, &config.rev); |
307 | if (ret) | |
bfd28b13 | 308 | return ret; |
bfd28b13 | 309 | |
bfd28b13 | 310 | dev->platform_data = &config; |
eec874ce RC |
311 | set_gpu_pdev(drm, to_platform_device(dev)); |
312 | ||
313 | info = adreno_info(config.rev); | |
314 | ||
315 | if (!info) { | |
316 | dev_warn(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n", | |
317 | config.rev.core, config.rev.major, | |
318 | config.rev.minor, config.rev.patchid); | |
319 | return -ENXIO; | |
320 | } | |
321 | ||
322 | DBG("Found GPU: %u.%u.%u.%u", config.rev.core, config.rev.major, | |
323 | config.rev.minor, config.rev.patchid); | |
324 | ||
c2052a4e JM |
325 | priv->is_a2xx = config.rev.core == 2; |
326 | ||
eec874ce RC |
327 | gpu = info->init(drm); |
328 | if (IS_ERR(gpu)) { | |
329 | dev_warn(drm->dev, "failed to load adreno gpu\n"); | |
330 | return PTR_ERR(gpu); | |
331 | } | |
332 | ||
333 | dev_set_drvdata(dev, gpu); | |
334 | ||
bfd28b13 RC |
335 | return 0; |
336 | } | |
337 | ||
338 | static void adreno_unbind(struct device *dev, struct device *master, | |
339 | void *data) | |
340 | { | |
eec874ce RC |
341 | struct msm_gpu *gpu = dev_get_drvdata(dev); |
342 | ||
343 | gpu->funcs->pm_suspend(gpu); | |
344 | gpu->funcs->destroy(gpu); | |
345 | ||
bfd28b13 RC |
346 | set_gpu_pdev(dev_get_drvdata(master), NULL); |
347 | } | |
348 | ||
349 | static const struct component_ops a3xx_ops = { | |
350 | .bind = adreno_bind, | |
351 | .unbind = adreno_unbind, | |
352 | }; | |
353 | ||
e6f6d63e JM |
354 | static void adreno_device_register_headless(void) |
355 | { | |
356 | /* on imx5, we don't have a top-level mdp/dpu node | |
357 | * this creates a dummy node for the driver for that case | |
358 | */ | |
359 | struct platform_device_info dummy_info = { | |
360 | .parent = NULL, | |
361 | .name = "msm", | |
362 | .id = -1, | |
363 | .res = NULL, | |
364 | .num_res = 0, | |
365 | .data = NULL, | |
366 | .size_data = 0, | |
367 | .dma_mask = ~0, | |
368 | }; | |
369 | platform_device_register_full(&dummy_info); | |
370 | } | |
371 | ||
bfd28b13 RC |
372 | static int adreno_probe(struct platform_device *pdev) |
373 | { | |
e6f6d63e JM |
374 | |
375 | int ret; | |
376 | ||
377 | ret = component_add(&pdev->dev, &a3xx_ops); | |
378 | if (ret) | |
379 | return ret; | |
380 | ||
381 | if (of_device_is_compatible(pdev->dev.of_node, "amd,imageon")) | |
382 | adreno_device_register_headless(); | |
383 | ||
384 | return 0; | |
bfd28b13 RC |
385 | } |
386 | ||
387 | static int adreno_remove(struct platform_device *pdev) | |
388 | { | |
389 | component_del(&pdev->dev, &a3xx_ops); | |
390 | return 0; | |
391 | } | |
392 | ||
393 | static const struct of_device_id dt_match[] = { | |
1db7afa4 | 394 | { .compatible = "qcom,adreno" }, |
bfd28b13 | 395 | { .compatible = "qcom,adreno-3xx" }, |
e6f6d63e JM |
396 | /* for compatibility with imx5 gpu: */ |
397 | { .compatible = "amd,imageon" }, | |
bfd28b13 RC |
398 | /* for backwards compat w/ downstream kgsl DT files: */ |
399 | { .compatible = "qcom,kgsl-3d0" }, | |
400 | {} | |
401 | }; | |
402 | ||
eeb75474 RC |
403 | #ifdef CONFIG_PM |
404 | static int adreno_resume(struct device *dev) | |
405 | { | |
406 | struct platform_device *pdev = to_platform_device(dev); | |
407 | struct msm_gpu *gpu = platform_get_drvdata(pdev); | |
408 | ||
409 | return gpu->funcs->pm_resume(gpu); | |
410 | } | |
411 | ||
412 | static int adreno_suspend(struct device *dev) | |
413 | { | |
414 | struct platform_device *pdev = to_platform_device(dev); | |
415 | struct msm_gpu *gpu = platform_get_drvdata(pdev); | |
416 | ||
417 | return gpu->funcs->pm_suspend(gpu); | |
418 | } | |
419 | #endif | |
420 | ||
421 | static const struct dev_pm_ops adreno_pm_ops = { | |
6666e1a6 | 422 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) |
eeb75474 RC |
423 | SET_RUNTIME_PM_OPS(adreno_suspend, adreno_resume, NULL) |
424 | }; | |
425 | ||
bfd28b13 RC |
426 | static struct platform_driver adreno_driver = { |
427 | .probe = adreno_probe, | |
428 | .remove = adreno_remove, | |
429 | .driver = { | |
430 | .name = "adreno", | |
431 | .of_match_table = dt_match, | |
eeb75474 | 432 | .pm = &adreno_pm_ops, |
bfd28b13 RC |
433 | }, |
434 | }; | |
435 | ||
436 | void __init adreno_register(void) | |
437 | { | |
438 | platform_driver_register(&adreno_driver); | |
439 | } | |
440 | ||
441 | void __exit adreno_unregister(void) | |
442 | { | |
443 | platform_driver_unregister(&adreno_driver); | |
444 | } |