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