linux/drivers/gpu/drm/tegra/vic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2015, NVIDIA Corporation.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/delay.h>
   8#include <linux/host1x.h>
   9#include <linux/iommu.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_device.h>
  13#include <linux/of_platform.h>
  14#include <linux/platform_device.h>
  15#include <linux/pm_runtime.h>
  16#include <linux/reset.h>
  17
  18#include <soc/tegra/pmc.h>
  19
  20#include "drm.h"
  21#include "falcon.h"
  22#include "vic.h"
  23
  24struct vic_config {
  25        const char *firmware;
  26        unsigned int version;
  27        bool supports_sid;
  28};
  29
  30struct vic {
  31        struct falcon falcon;
  32        bool booted;
  33
  34        void __iomem *regs;
  35        struct tegra_drm_client client;
  36        struct host1x_channel *channel;
  37        struct device *dev;
  38        struct clk *clk;
  39        struct reset_control *rst;
  40
  41        /* Platform configuration */
  42        const struct vic_config *config;
  43};
  44
  45static inline struct vic *to_vic(struct tegra_drm_client *client)
  46{
  47        return container_of(client, struct vic, client);
  48}
  49
  50static void vic_writel(struct vic *vic, u32 value, unsigned int offset)
  51{
  52        writel(value, vic->regs + offset);
  53}
  54
  55static int vic_runtime_resume(struct device *dev)
  56{
  57        struct vic *vic = dev_get_drvdata(dev);
  58        int err;
  59
  60        err = clk_prepare_enable(vic->clk);
  61        if (err < 0)
  62                return err;
  63
  64        usleep_range(10, 20);
  65
  66        err = reset_control_deassert(vic->rst);
  67        if (err < 0)
  68                goto disable;
  69
  70        usleep_range(10, 20);
  71
  72        return 0;
  73
  74disable:
  75        clk_disable_unprepare(vic->clk);
  76        return err;
  77}
  78
  79static int vic_runtime_suspend(struct device *dev)
  80{
  81        struct vic *vic = dev_get_drvdata(dev);
  82        int err;
  83
  84        err = reset_control_assert(vic->rst);
  85        if (err < 0)
  86                return err;
  87
  88        usleep_range(2000, 4000);
  89
  90        clk_disable_unprepare(vic->clk);
  91
  92        vic->booted = false;
  93
  94        return 0;
  95}
  96
  97static int vic_boot(struct vic *vic)
  98{
  99#ifdef CONFIG_IOMMU_API
 100        struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
 101#endif
 102        u32 fce_ucode_size, fce_bin_data_offset;
 103        void *hdr;
 104        int err = 0;
 105
 106        if (vic->booted)
 107                return 0;
 108
 109#ifdef CONFIG_IOMMU_API
 110        if (vic->config->supports_sid && spec) {
 111                u32 value;
 112
 113                value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
 114                        TRANSCFG_ATT(0, TRANSCFG_SID_HW);
 115                vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
 116
 117                if (spec->num_ids > 0) {
 118                        value = spec->ids[0] & 0xffff;
 119
 120                        vic_writel(vic, value, VIC_THI_STREAMID0);
 121                        vic_writel(vic, value, VIC_THI_STREAMID1);
 122                }
 123        }
 124#endif
 125
 126        /* setup clockgating registers */
 127        vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
 128                        CG_IDLE_CG_EN |
 129                        CG_WAKEUP_DLY_CNT(4),
 130                   NV_PVIC_MISC_PRI_VIC_CG);
 131
 132        err = falcon_boot(&vic->falcon);
 133        if (err < 0)
 134                return err;
 135
 136        hdr = vic->falcon.firmware.virt;
 137        fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
 138        hdr = vic->falcon.firmware.virt +
 139                *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
 140        fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
 141
 142        falcon_execute_method(&vic->falcon, VIC_SET_APPLICATION_ID, 1);
 143        falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
 144                              fce_ucode_size);
 145        falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
 146                              (vic->falcon.firmware.iova + fce_bin_data_offset)
 147                                >> 8);
 148
 149        err = falcon_wait_idle(&vic->falcon);
 150        if (err < 0) {
 151                dev_err(vic->dev,
 152                        "failed to set application ID and FCE base\n");
 153                return err;
 154        }
 155
 156        vic->booted = true;
 157
 158        return 0;
 159}
 160
 161static int vic_init(struct host1x_client *client)
 162{
 163        struct tegra_drm_client *drm = host1x_to_drm_client(client);
 164        struct drm_device *dev = dev_get_drvdata(client->parent);
 165        struct tegra_drm *tegra = dev->dev_private;
 166        struct vic *vic = to_vic(drm);
 167        int err;
 168
 169        err = host1x_client_iommu_attach(client);
 170        if (err < 0 && err != -ENODEV) {
 171                dev_err(vic->dev, "failed to attach to domain: %d\n", err);
 172                return err;
 173        }
 174
 175        vic->channel = host1x_channel_request(client);
 176        if (!vic->channel) {
 177                err = -ENOMEM;
 178                goto detach;
 179        }
 180
 181        client->syncpts[0] = host1x_syncpt_request(client, 0);
 182        if (!client->syncpts[0]) {
 183                err = -ENOMEM;
 184                goto free_channel;
 185        }
 186
 187        err = tegra_drm_register_client(tegra, drm);
 188        if (err < 0)
 189                goto free_syncpt;
 190
 191        /*
 192         * Inherit the DMA parameters (such as maximum segment size) from the
 193         * parent device.
 194         */
 195        client->dev->dma_parms = client->parent->dma_parms;
 196
 197        return 0;
 198
 199free_syncpt:
 200        host1x_syncpt_free(client->syncpts[0]);
 201free_channel:
 202        host1x_channel_put(vic->channel);
 203detach:
 204        host1x_client_iommu_detach(client);
 205
 206        return err;
 207}
 208
 209static int vic_exit(struct host1x_client *client)
 210{
 211        struct tegra_drm_client *drm = host1x_to_drm_client(client);
 212        struct drm_device *dev = dev_get_drvdata(client->parent);
 213        struct tegra_drm *tegra = dev->dev_private;
 214        struct vic *vic = to_vic(drm);
 215        int err;
 216
 217        /* avoid a dangling pointer just in case this disappears */
 218        client->dev->dma_parms = NULL;
 219
 220        err = tegra_drm_unregister_client(tegra, drm);
 221        if (err < 0)
 222                return err;
 223
 224        host1x_syncpt_free(client->syncpts[0]);
 225        host1x_channel_put(vic->channel);
 226        host1x_client_iommu_detach(client);
 227
 228        if (client->group) {
 229                dma_unmap_single(vic->dev, vic->falcon.firmware.phys,
 230                                 vic->falcon.firmware.size, DMA_TO_DEVICE);
 231                tegra_drm_free(tegra, vic->falcon.firmware.size,
 232                               vic->falcon.firmware.virt,
 233                               vic->falcon.firmware.iova);
 234        } else {
 235                dma_free_coherent(vic->dev, vic->falcon.firmware.size,
 236                                  vic->falcon.firmware.virt,
 237                                  vic->falcon.firmware.iova);
 238        }
 239
 240        return 0;
 241}
 242
 243static const struct host1x_client_ops vic_client_ops = {
 244        .init = vic_init,
 245        .exit = vic_exit,
 246};
 247
 248static int vic_load_firmware(struct vic *vic)
 249{
 250        struct host1x_client *client = &vic->client.base;
 251        struct tegra_drm *tegra = vic->client.drm;
 252        dma_addr_t iova;
 253        size_t size;
 254        void *virt;
 255        int err;
 256
 257        if (vic->falcon.firmware.virt)
 258                return 0;
 259
 260        err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
 261        if (err < 0)
 262                return err;
 263
 264        size = vic->falcon.firmware.size;
 265
 266        if (!client->group) {
 267                virt = dma_alloc_coherent(vic->dev, size, &iova, GFP_KERNEL);
 268
 269                err = dma_mapping_error(vic->dev, iova);
 270                if (err < 0)
 271                        return err;
 272        } else {
 273                virt = tegra_drm_alloc(tegra, size, &iova);
 274        }
 275
 276        vic->falcon.firmware.virt = virt;
 277        vic->falcon.firmware.iova = iova;
 278
 279        err = falcon_load_firmware(&vic->falcon);
 280        if (err < 0)
 281                goto cleanup;
 282
 283        /*
 284         * In this case we have received an IOVA from the shared domain, so we
 285         * need to make sure to get the physical address so that the DMA API
 286         * knows what memory pages to flush the cache for.
 287         */
 288        if (client->group) {
 289                dma_addr_t phys;
 290
 291                phys = dma_map_single(vic->dev, virt, size, DMA_TO_DEVICE);
 292
 293                err = dma_mapping_error(vic->dev, phys);
 294                if (err < 0)
 295                        goto cleanup;
 296
 297                vic->falcon.firmware.phys = phys;
 298        }
 299
 300        return 0;
 301
 302cleanup:
 303        if (!client->group)
 304                dma_free_coherent(vic->dev, size, virt, iova);
 305        else
 306                tegra_drm_free(tegra, size, virt, iova);
 307
 308        return err;
 309}
 310
 311static int vic_open_channel(struct tegra_drm_client *client,
 312                            struct tegra_drm_context *context)
 313{
 314        struct vic *vic = to_vic(client);
 315        int err;
 316
 317        err = pm_runtime_get_sync(vic->dev);
 318        if (err < 0)
 319                return err;
 320
 321        err = vic_load_firmware(vic);
 322        if (err < 0)
 323                goto rpm_put;
 324
 325        err = vic_boot(vic);
 326        if (err < 0)
 327                goto rpm_put;
 328
 329        context->channel = host1x_channel_get(vic->channel);
 330        if (!context->channel) {
 331                err = -ENOMEM;
 332                goto rpm_put;
 333        }
 334
 335        return 0;
 336
 337rpm_put:
 338        pm_runtime_put(vic->dev);
 339        return err;
 340}
 341
 342static void vic_close_channel(struct tegra_drm_context *context)
 343{
 344        struct vic *vic = to_vic(context->client);
 345
 346        host1x_channel_put(context->channel);
 347
 348        pm_runtime_put(vic->dev);
 349}
 350
 351static const struct tegra_drm_client_ops vic_ops = {
 352        .open_channel = vic_open_channel,
 353        .close_channel = vic_close_channel,
 354        .submit = tegra_drm_submit,
 355};
 356
 357#define NVIDIA_TEGRA_124_VIC_FIRMWARE "nvidia/tegra124/vic03_ucode.bin"
 358
 359static const struct vic_config vic_t124_config = {
 360        .firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE,
 361        .version = 0x40,
 362        .supports_sid = false,
 363};
 364
 365#define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin"
 366
 367static const struct vic_config vic_t210_config = {
 368        .firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE,
 369        .version = 0x21,
 370        .supports_sid = false,
 371};
 372
 373#define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin"
 374
 375static const struct vic_config vic_t186_config = {
 376        .firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
 377        .version = 0x18,
 378        .supports_sid = true,
 379};
 380
 381#define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin"
 382
 383static const struct vic_config vic_t194_config = {
 384        .firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE,
 385        .version = 0x19,
 386        .supports_sid = true,
 387};
 388
 389static const struct of_device_id tegra_vic_of_match[] = {
 390        { .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config },
 391        { .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config },
 392        { .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
 393        { .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
 394        { },
 395};
 396MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
 397
 398static int vic_probe(struct platform_device *pdev)
 399{
 400        struct device *dev = &pdev->dev;
 401        struct host1x_syncpt **syncpts;
 402        struct resource *regs;
 403        struct vic *vic;
 404        int err;
 405
 406        /* inherit DMA mask from host1x parent */
 407        err = dma_coerce_mask_and_coherent(dev, *dev->parent->dma_mask);
 408        if (err < 0) {
 409                dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
 410                return err;
 411        }
 412
 413        vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL);
 414        if (!vic)
 415                return -ENOMEM;
 416
 417        vic->config = of_device_get_match_data(dev);
 418
 419        syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
 420        if (!syncpts)
 421                return -ENOMEM;
 422
 423        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 424        if (!regs) {
 425                dev_err(&pdev->dev, "failed to get registers\n");
 426                return -ENXIO;
 427        }
 428
 429        vic->regs = devm_ioremap_resource(dev, regs);
 430        if (IS_ERR(vic->regs))
 431                return PTR_ERR(vic->regs);
 432
 433        vic->clk = devm_clk_get(dev, NULL);
 434        if (IS_ERR(vic->clk)) {
 435                dev_err(&pdev->dev, "failed to get clock\n");
 436                return PTR_ERR(vic->clk);
 437        }
 438
 439        if (!dev->pm_domain) {
 440                vic->rst = devm_reset_control_get(dev, "vic");
 441                if (IS_ERR(vic->rst)) {
 442                        dev_err(&pdev->dev, "failed to get reset\n");
 443                        return PTR_ERR(vic->rst);
 444                }
 445        }
 446
 447        vic->falcon.dev = dev;
 448        vic->falcon.regs = vic->regs;
 449
 450        err = falcon_init(&vic->falcon);
 451        if (err < 0)
 452                return err;
 453
 454        platform_set_drvdata(pdev, vic);
 455
 456        INIT_LIST_HEAD(&vic->client.base.list);
 457        vic->client.base.ops = &vic_client_ops;
 458        vic->client.base.dev = dev;
 459        vic->client.base.class = HOST1X_CLASS_VIC;
 460        vic->client.base.syncpts = syncpts;
 461        vic->client.base.num_syncpts = 1;
 462        vic->dev = dev;
 463
 464        INIT_LIST_HEAD(&vic->client.list);
 465        vic->client.version = vic->config->version;
 466        vic->client.ops = &vic_ops;
 467
 468        err = host1x_client_register(&vic->client.base);
 469        if (err < 0) {
 470                dev_err(dev, "failed to register host1x client: %d\n", err);
 471                goto exit_falcon;
 472        }
 473
 474        pm_runtime_enable(&pdev->dev);
 475        if (!pm_runtime_enabled(&pdev->dev)) {
 476                err = vic_runtime_resume(&pdev->dev);
 477                if (err < 0)
 478                        goto unregister_client;
 479        }
 480
 481        return 0;
 482
 483unregister_client:
 484        host1x_client_unregister(&vic->client.base);
 485exit_falcon:
 486        falcon_exit(&vic->falcon);
 487
 488        return err;
 489}
 490
 491static int vic_remove(struct platform_device *pdev)
 492{
 493        struct vic *vic = platform_get_drvdata(pdev);
 494        int err;
 495
 496        err = host1x_client_unregister(&vic->client.base);
 497        if (err < 0) {
 498                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
 499                        err);
 500                return err;
 501        }
 502
 503        if (pm_runtime_enabled(&pdev->dev))
 504                pm_runtime_disable(&pdev->dev);
 505        else
 506                vic_runtime_suspend(&pdev->dev);
 507
 508        falcon_exit(&vic->falcon);
 509
 510        return 0;
 511}
 512
 513static const struct dev_pm_ops vic_pm_ops = {
 514        SET_RUNTIME_PM_OPS(vic_runtime_suspend, vic_runtime_resume, NULL)
 515};
 516
 517struct platform_driver tegra_vic_driver = {
 518        .driver = {
 519                .name = "tegra-vic",
 520                .of_match_table = tegra_vic_of_match,
 521                .pm = &vic_pm_ops
 522        },
 523        .probe = vic_probe,
 524        .remove = vic_remove,
 525};
 526
 527#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)
 528MODULE_FIRMWARE(NVIDIA_TEGRA_124_VIC_FIRMWARE);
 529#endif
 530#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
 531MODULE_FIRMWARE(NVIDIA_TEGRA_210_VIC_FIRMWARE);
 532#endif
 533#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
 534MODULE_FIRMWARE(NVIDIA_TEGRA_186_VIC_FIRMWARE);
 535#endif
 536#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
 537MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
 538#endif
 539