e20c9284c8
The Adreno 510 GPU is a stripped version of the Adreno 5xx, found in low-end SoCs like 8x56 and 8x76, which has 256K of GMEM, with no GPMU nor ZAP. Also, since the Adreno 5xx part of this driver seems to be developed with high-end Adreno GPUs in mind, and since this is a lower end one, add a comment making clear which GPUs which support is not implemented yet is not using the GPMU related hw init code, so that future developers will not go crazy with that. By the way, the lower end Adreno GPUs with no GPMU are: A505/A506/A510 (usually no ZAP firmware) A508/A509/A512 (usually with ZAP firmware) Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com> Signed-off-by: Rob Clark <robdclark@chromium.org>
479 lines
11 KiB
C
479 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2013-2014 Red Hat
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
*
|
|
* Copyright (c) 2014,2017 The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include "adreno_gpu.h"
|
|
|
|
#define ANY_ID 0xff
|
|
|
|
bool hang_debug = false;
|
|
MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)");
|
|
module_param_named(hang_debug, hang_debug, bool, 0600);
|
|
|
|
static const struct adreno_info gpulist[] = {
|
|
{
|
|
.rev = ADRENO_REV(2, 0, 0, 0),
|
|
.revn = 200,
|
|
.name = "A200",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "yamato_pm4.fw",
|
|
[ADRENO_FW_PFP] = "yamato_pfp.fw",
|
|
},
|
|
.gmem = SZ_256K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a2xx_gpu_init,
|
|
}, { /* a200 on i.mx51 has only 128kib gmem */
|
|
.rev = ADRENO_REV(2, 0, 0, 1),
|
|
.revn = 201,
|
|
.name = "A200",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "yamato_pm4.fw",
|
|
[ADRENO_FW_PFP] = "yamato_pfp.fw",
|
|
},
|
|
.gmem = SZ_128K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a2xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(2, 2, 0, ANY_ID),
|
|
.revn = 220,
|
|
.name = "A220",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "leia_pm4_470.fw",
|
|
[ADRENO_FW_PFP] = "leia_pfp_470.fw",
|
|
},
|
|
.gmem = SZ_512K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a2xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(3, 0, 5, ANY_ID),
|
|
.revn = 305,
|
|
.name = "A305",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a300_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a300_pfp.fw",
|
|
},
|
|
.gmem = SZ_256K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a3xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(3, 0, 6, 0),
|
|
.revn = 307, /* because a305c is revn==306 */
|
|
.name = "A306",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a300_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a300_pfp.fw",
|
|
},
|
|
.gmem = SZ_128K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a3xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID),
|
|
.revn = 320,
|
|
.name = "A320",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a300_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a300_pfp.fw",
|
|
},
|
|
.gmem = SZ_512K,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a3xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(3, 3, 0, ANY_ID),
|
|
.revn = 330,
|
|
.name = "A330",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a330_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a330_pfp.fw",
|
|
},
|
|
.gmem = SZ_1M,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a3xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(4, 2, 0, ANY_ID),
|
|
.revn = 420,
|
|
.name = "A420",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a420_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a420_pfp.fw",
|
|
},
|
|
.gmem = (SZ_1M + SZ_512K),
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a4xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(4, 3, 0, ANY_ID),
|
|
.revn = 430,
|
|
.name = "A430",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a420_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a420_pfp.fw",
|
|
},
|
|
.gmem = (SZ_1M + SZ_512K),
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a4xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(5, 1, 0, ANY_ID),
|
|
.revn = 510,
|
|
.name = "A510",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a530_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a530_pfp.fw",
|
|
},
|
|
.gmem = SZ_256K,
|
|
/*
|
|
* Increase inactive period to 250 to avoid bouncing
|
|
* the GDSC which appears to make it grumpy
|
|
*/
|
|
.inactive_period = 250,
|
|
.init = a5xx_gpu_init,
|
|
}, {
|
|
.rev = ADRENO_REV(5, 3, 0, 2),
|
|
.revn = 530,
|
|
.name = "A530",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a530_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a530_pfp.fw",
|
|
[ADRENO_FW_GPMU] = "a530v3_gpmu.fw2",
|
|
},
|
|
.gmem = SZ_1M,
|
|
/*
|
|
* Increase inactive period to 250 to avoid bouncing
|
|
* the GDSC which appears to make it grumpy
|
|
*/
|
|
.inactive_period = 250,
|
|
.quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI |
|
|
ADRENO_QUIRK_FAULT_DETECT_MASK,
|
|
.init = a5xx_gpu_init,
|
|
.zapfw = "a530_zap.mdt",
|
|
}, {
|
|
.rev = ADRENO_REV(5, 4, 0, 2),
|
|
.revn = 540,
|
|
.name = "A540",
|
|
.fw = {
|
|
[ADRENO_FW_PM4] = "a530_pm4.fw",
|
|
[ADRENO_FW_PFP] = "a530_pfp.fw",
|
|
[ADRENO_FW_GPMU] = "a540_gpmu.fw2",
|
|
},
|
|
.gmem = SZ_1M,
|
|
/*
|
|
* Increase inactive period to 250 to avoid bouncing
|
|
* the GDSC which appears to make it grumpy
|
|
*/
|
|
.inactive_period = 250,
|
|
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
|
|
.init = a5xx_gpu_init,
|
|
.zapfw = "a540_zap.mdt",
|
|
}, {
|
|
.rev = ADRENO_REV(6, 3, 0, ANY_ID),
|
|
.revn = 630,
|
|
.name = "A630",
|
|
.fw = {
|
|
[ADRENO_FW_SQE] = "a630_sqe.fw",
|
|
[ADRENO_FW_GMU] = "a630_gmu.bin",
|
|
},
|
|
.gmem = SZ_1M,
|
|
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
|
|
.init = a6xx_gpu_init,
|
|
.zapfw = "a630_zap.mdt",
|
|
},
|
|
};
|
|
|
|
MODULE_FIRMWARE("qcom/a300_pm4.fw");
|
|
MODULE_FIRMWARE("qcom/a300_pfp.fw");
|
|
MODULE_FIRMWARE("qcom/a330_pm4.fw");
|
|
MODULE_FIRMWARE("qcom/a330_pfp.fw");
|
|
MODULE_FIRMWARE("qcom/a420_pm4.fw");
|
|
MODULE_FIRMWARE("qcom/a420_pfp.fw");
|
|
MODULE_FIRMWARE("qcom/a530_pm4.fw");
|
|
MODULE_FIRMWARE("qcom/a530_pfp.fw");
|
|
MODULE_FIRMWARE("qcom/a530v3_gpmu.fw2");
|
|
MODULE_FIRMWARE("qcom/a530_zap.mdt");
|
|
MODULE_FIRMWARE("qcom/a530_zap.b00");
|
|
MODULE_FIRMWARE("qcom/a530_zap.b01");
|
|
MODULE_FIRMWARE("qcom/a530_zap.b02");
|
|
MODULE_FIRMWARE("qcom/a630_sqe.fw");
|
|
MODULE_FIRMWARE("qcom/a630_gmu.bin");
|
|
MODULE_FIRMWARE("qcom/a630_zap.mbn");
|
|
|
|
static inline bool _rev_match(uint8_t entry, uint8_t id)
|
|
{
|
|
return (entry == ANY_ID) || (entry == id);
|
|
}
|
|
|
|
const struct adreno_info *adreno_info(struct adreno_rev rev)
|
|
{
|
|
int i;
|
|
|
|
/* identify gpu: */
|
|
for (i = 0; i < ARRAY_SIZE(gpulist); i++) {
|
|
const struct adreno_info *info = &gpulist[i];
|
|
if (_rev_match(info->rev.core, rev.core) &&
|
|
_rev_match(info->rev.major, rev.major) &&
|
|
_rev_match(info->rev.minor, rev.minor) &&
|
|
_rev_match(info->rev.patchid, rev.patchid))
|
|
return info;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
|
|
{
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
struct platform_device *pdev = priv->gpu_pdev;
|
|
struct msm_gpu *gpu = NULL;
|
|
struct adreno_gpu *adreno_gpu;
|
|
int ret;
|
|
|
|
if (pdev)
|
|
gpu = platform_get_drvdata(pdev);
|
|
|
|
if (!gpu) {
|
|
dev_err_once(dev->dev, "no GPU device was found\n");
|
|
return NULL;
|
|
}
|
|
|
|
adreno_gpu = to_adreno_gpu(gpu);
|
|
|
|
/*
|
|
* The number one reason for HW init to fail is if the firmware isn't
|
|
* loaded yet. Try that first and don't bother continuing on
|
|
* otherwise
|
|
*/
|
|
|
|
ret = adreno_load_fw(adreno_gpu);
|
|
if (ret)
|
|
return NULL;
|
|
|
|
/* Make sure pm runtime is active and reset any previous errors */
|
|
pm_runtime_set_active(&pdev->dev);
|
|
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
|
if (ret < 0) {
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
DRM_DEV_ERROR(dev->dev, "Couldn't power up the GPU: %d\n", ret);
|
|
return NULL;
|
|
}
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
ret = msm_gpu_hw_init(gpu);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
pm_runtime_put_autosuspend(&pdev->dev);
|
|
if (ret) {
|
|
DRM_DEV_ERROR(dev->dev, "gpu hw init failed: %d\n", ret);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
if (gpu->funcs->debugfs_init) {
|
|
gpu->funcs->debugfs_init(gpu, dev->primary);
|
|
gpu->funcs->debugfs_init(gpu, dev->render);
|
|
}
|
|
#endif
|
|
|
|
return gpu;
|
|
}
|
|
|
|
static void set_gpu_pdev(struct drm_device *dev,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
priv->gpu_pdev = pdev;
|
|
}
|
|
|
|
static int find_chipid(struct device *dev, struct adreno_rev *rev)
|
|
{
|
|
struct device_node *node = dev->of_node;
|
|
const char *compat;
|
|
int ret;
|
|
u32 chipid;
|
|
|
|
/* first search the compat strings for qcom,adreno-XYZ.W: */
|
|
ret = of_property_read_string_index(node, "compatible", 0, &compat);
|
|
if (ret == 0) {
|
|
unsigned int r, patch;
|
|
|
|
if (sscanf(compat, "qcom,adreno-%u.%u", &r, &patch) == 2 ||
|
|
sscanf(compat, "amd,imageon-%u.%u", &r, &patch) == 2) {
|
|
rev->core = r / 100;
|
|
r %= 100;
|
|
rev->major = r / 10;
|
|
r %= 10;
|
|
rev->minor = r;
|
|
rev->patchid = patch;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* and if that fails, fall back to legacy "qcom,chipid" property: */
|
|
ret = of_property_read_u32(node, "qcom,chipid", &chipid);
|
|
if (ret) {
|
|
DRM_DEV_ERROR(dev, "could not parse qcom,chipid: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
rev->core = (chipid >> 24) & 0xff;
|
|
rev->major = (chipid >> 16) & 0xff;
|
|
rev->minor = (chipid >> 8) & 0xff;
|
|
rev->patchid = (chipid & 0xff);
|
|
|
|
dev_warn(dev, "Using legacy qcom,chipid binding!\n");
|
|
dev_warn(dev, "Use compatible qcom,adreno-%u%u%u.%u instead.\n",
|
|
rev->core, rev->major, rev->minor, rev->patchid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adreno_bind(struct device *dev, struct device *master, void *data)
|
|
{
|
|
static struct adreno_platform_config config = {};
|
|
const struct adreno_info *info;
|
|
struct drm_device *drm = dev_get_drvdata(master);
|
|
struct msm_drm_private *priv = drm->dev_private;
|
|
struct msm_gpu *gpu;
|
|
int ret;
|
|
|
|
ret = find_chipid(dev, &config.rev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dev->platform_data = &config;
|
|
set_gpu_pdev(drm, to_platform_device(dev));
|
|
|
|
info = adreno_info(config.rev);
|
|
|
|
if (!info) {
|
|
dev_warn(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n",
|
|
config.rev.core, config.rev.major,
|
|
config.rev.minor, config.rev.patchid);
|
|
return -ENXIO;
|
|
}
|
|
|
|
DBG("Found GPU: %u.%u.%u.%u", config.rev.core, config.rev.major,
|
|
config.rev.minor, config.rev.patchid);
|
|
|
|
priv->is_a2xx = config.rev.core == 2;
|
|
|
|
gpu = info->init(drm);
|
|
if (IS_ERR(gpu)) {
|
|
dev_warn(drm->dev, "failed to load adreno gpu\n");
|
|
return PTR_ERR(gpu);
|
|
}
|
|
|
|
dev_set_drvdata(dev, gpu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void adreno_unbind(struct device *dev, struct device *master,
|
|
void *data)
|
|
{
|
|
struct msm_gpu *gpu = dev_get_drvdata(dev);
|
|
|
|
pm_runtime_force_suspend(dev);
|
|
gpu->funcs->destroy(gpu);
|
|
|
|
set_gpu_pdev(dev_get_drvdata(master), NULL);
|
|
}
|
|
|
|
static const struct component_ops a3xx_ops = {
|
|
.bind = adreno_bind,
|
|
.unbind = adreno_unbind,
|
|
};
|
|
|
|
static void adreno_device_register_headless(void)
|
|
{
|
|
/* on imx5, we don't have a top-level mdp/dpu node
|
|
* this creates a dummy node for the driver for that case
|
|
*/
|
|
struct platform_device_info dummy_info = {
|
|
.parent = NULL,
|
|
.name = "msm",
|
|
.id = -1,
|
|
.res = NULL,
|
|
.num_res = 0,
|
|
.data = NULL,
|
|
.size_data = 0,
|
|
.dma_mask = ~0,
|
|
};
|
|
platform_device_register_full(&dummy_info);
|
|
}
|
|
|
|
static int adreno_probe(struct platform_device *pdev)
|
|
{
|
|
|
|
int ret;
|
|
|
|
ret = component_add(&pdev->dev, &a3xx_ops);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (of_device_is_compatible(pdev->dev.of_node, "amd,imageon"))
|
|
adreno_device_register_headless();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adreno_remove(struct platform_device *pdev)
|
|
{
|
|
component_del(&pdev->dev, &a3xx_ops);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id dt_match[] = {
|
|
{ .compatible = "qcom,adreno" },
|
|
{ .compatible = "qcom,adreno-3xx" },
|
|
/* for compatibility with imx5 gpu: */
|
|
{ .compatible = "amd,imageon" },
|
|
/* for backwards compat w/ downstream kgsl DT files: */
|
|
{ .compatible = "qcom,kgsl-3d0" },
|
|
{}
|
|
};
|
|
|
|
#ifdef CONFIG_PM
|
|
static int adreno_resume(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct msm_gpu *gpu = platform_get_drvdata(pdev);
|
|
|
|
return gpu->funcs->pm_resume(gpu);
|
|
}
|
|
|
|
static int adreno_suspend(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct msm_gpu *gpu = platform_get_drvdata(pdev);
|
|
|
|
return gpu->funcs->pm_suspend(gpu);
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops adreno_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
|
SET_RUNTIME_PM_OPS(adreno_suspend, adreno_resume, NULL)
|
|
};
|
|
|
|
static struct platform_driver adreno_driver = {
|
|
.probe = adreno_probe,
|
|
.remove = adreno_remove,
|
|
.driver = {
|
|
.name = "adreno",
|
|
.of_match_table = dt_match,
|
|
.pm = &adreno_pm_ops,
|
|
},
|
|
};
|
|
|
|
void __init adreno_register(void)
|
|
{
|
|
platform_driver_register(&adreno_driver);
|
|
}
|
|
|
|
void __exit adreno_unregister(void)
|
|
{
|
|
platform_driver_unregister(&adreno_driver);
|
|
}
|