linux/drivers/gpu/drm/tegra/vic.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015, NVIDIA Corporation.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/host1x.h>
  11#include <linux/iommu.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/of_device.h>
  15#include <linux/of_platform.h>
  16#include <linux/platform_device.h>
  17#include <linux/pm_runtime.h>
  18#include <linux/reset.h>
  19
  20#include <soc/tegra/pmc.h>
  21
  22#include "drm.h"
  23#include "falcon.h"
  24#include "vic.h"
  25
  26struct vic_config {
  27        const char *firmware;
  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 iommu_domain *domain;
  38        struct device *dev;
  39        struct clk *clk;
  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
  59        return clk_prepare_enable(vic->clk);
  60}
  61
  62static int vic_runtime_suspend(struct device *dev)
  63{
  64        struct vic *vic = dev_get_drvdata(dev);
  65
  66        clk_disable_unprepare(vic->clk);
  67
  68        vic->booted = false;
  69
  70        return 0;
  71}
  72
  73static int vic_boot(struct vic *vic)
  74{
  75        u32 fce_ucode_size, fce_bin_data_offset;
  76        void *hdr;
  77        int err = 0;
  78
  79        if (vic->booted)
  80                return 0;
  81
  82        /* setup clockgating registers */
  83        vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
  84                        CG_IDLE_CG_EN |
  85                        CG_WAKEUP_DLY_CNT(4),
  86                   NV_PVIC_MISC_PRI_VIC_CG);
  87
  88        err = falcon_boot(&vic->falcon);
  89        if (err < 0)
  90                return err;
  91
  92        hdr = vic->falcon.firmware.vaddr;
  93        fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
  94        hdr = vic->falcon.firmware.vaddr +
  95                *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
  96        fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
  97
  98        falcon_execute_method(&vic->falcon, VIC_SET_APPLICATION_ID, 1);
  99        falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
 100                              fce_ucode_size);
 101        falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
 102                              (vic->falcon.firmware.paddr + fce_bin_data_offset)
 103                                >> 8);
 104
 105        err = falcon_wait_idle(&vic->falcon);
 106        if (err < 0) {
 107                dev_err(vic->dev,
 108                        "failed to set application ID and FCE base\n");
 109                return err;
 110        }
 111
 112        vic->booted = true;
 113
 114        return 0;
 115}
 116
 117static void *vic_falcon_alloc(struct falcon *falcon, size_t size,
 118                               dma_addr_t *iova)
 119{
 120        struct tegra_drm *tegra = falcon->data;
 121
 122        return tegra_drm_alloc(tegra, size, iova);
 123}
 124
 125static void vic_falcon_free(struct falcon *falcon, size_t size,
 126                            dma_addr_t iova, void *va)
 127{
 128        struct tegra_drm *tegra = falcon->data;
 129
 130        return tegra_drm_free(tegra, size, va, iova);
 131}
 132
 133static const struct falcon_ops vic_falcon_ops = {
 134        .alloc = vic_falcon_alloc,
 135        .free = vic_falcon_free
 136};
 137
 138static int vic_init(struct host1x_client *client)
 139{
 140        struct tegra_drm_client *drm = host1x_to_drm_client(client);
 141        struct drm_device *dev = dev_get_drvdata(client->parent);
 142        struct tegra_drm *tegra = dev->dev_private;
 143        struct vic *vic = to_vic(drm);
 144        int err;
 145
 146        if (tegra->domain) {
 147                err = iommu_attach_device(tegra->domain, vic->dev);
 148                if (err < 0) {
 149                        dev_err(vic->dev, "failed to attach to domain: %d\n",
 150                                err);
 151                        return err;
 152                }
 153
 154                vic->domain = tegra->domain;
 155        }
 156
 157        if (!vic->falcon.data) {
 158                vic->falcon.data = tegra;
 159                err = falcon_load_firmware(&vic->falcon);
 160                if (err < 0)
 161                        goto detach_device;
 162        }
 163
 164        vic->channel = host1x_channel_request(client->dev);
 165        if (!vic->channel) {
 166                err = -ENOMEM;
 167                goto detach_device;
 168        }
 169
 170        client->syncpts[0] = host1x_syncpt_request(client->dev, 0);
 171        if (!client->syncpts[0]) {
 172                err = -ENOMEM;
 173                goto free_channel;
 174        }
 175
 176        err = tegra_drm_register_client(tegra, drm);
 177        if (err < 0)
 178                goto free_syncpt;
 179
 180        return 0;
 181
 182free_syncpt:
 183        host1x_syncpt_free(client->syncpts[0]);
 184free_channel:
 185        host1x_channel_free(vic->channel);
 186detach_device:
 187        if (tegra->domain)
 188                iommu_detach_device(tegra->domain, vic->dev);
 189
 190        return err;
 191}
 192
 193static int vic_exit(struct host1x_client *client)
 194{
 195        struct tegra_drm_client *drm = host1x_to_drm_client(client);
 196        struct drm_device *dev = dev_get_drvdata(client->parent);
 197        struct tegra_drm *tegra = dev->dev_private;
 198        struct vic *vic = to_vic(drm);
 199        int err;
 200
 201        err = tegra_drm_unregister_client(tegra, drm);
 202        if (err < 0)
 203                return err;
 204
 205        host1x_syncpt_free(client->syncpts[0]);
 206        host1x_channel_free(vic->channel);
 207
 208        if (vic->domain) {
 209                iommu_detach_device(vic->domain, vic->dev);
 210                vic->domain = NULL;
 211        }
 212
 213        return 0;
 214}
 215
 216static const struct host1x_client_ops vic_client_ops = {
 217        .init = vic_init,
 218        .exit = vic_exit,
 219};
 220
 221static int vic_open_channel(struct tegra_drm_client *client,
 222                            struct tegra_drm_context *context)
 223{
 224        struct vic *vic = to_vic(client);
 225        int err;
 226
 227        err = pm_runtime_get_sync(vic->dev);
 228        if (err < 0)
 229                return err;
 230
 231        err = vic_boot(vic);
 232        if (err < 0) {
 233                pm_runtime_put(vic->dev);
 234                return err;
 235        }
 236
 237        context->channel = host1x_channel_get(vic->channel);
 238        if (!context->channel) {
 239                pm_runtime_put(vic->dev);
 240                return -ENOMEM;
 241        }
 242
 243        return 0;
 244}
 245
 246static void vic_close_channel(struct tegra_drm_context *context)
 247{
 248        struct vic *vic = to_vic(context->client);
 249
 250        host1x_channel_put(context->channel);
 251
 252        pm_runtime_put(vic->dev);
 253}
 254
 255static const struct tegra_drm_client_ops vic_ops = {
 256        .open_channel = vic_open_channel,
 257        .close_channel = vic_close_channel,
 258        .submit = tegra_drm_submit,
 259};
 260
 261static const struct vic_config vic_t124_config = {
 262        .firmware = "nvidia/tegra124/vic03_ucode.bin",
 263};
 264
 265static const struct vic_config vic_t210_config = {
 266        .firmware = "nvidia/tegra210/vic04_ucode.bin",
 267};
 268
 269static const struct of_device_id vic_match[] = {
 270        { .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config },
 271        { .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config },
 272        { },
 273};
 274
 275static int vic_probe(struct platform_device *pdev)
 276{
 277        struct vic_config *vic_config = NULL;
 278        struct device *dev = &pdev->dev;
 279        struct host1x_syncpt **syncpts;
 280        struct resource *regs;
 281        const struct of_device_id *match;
 282        struct vic *vic;
 283        int err;
 284
 285        match = of_match_device(vic_match, dev);
 286        vic_config = (struct vic_config *)match->data;
 287
 288        vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL);
 289        if (!vic)
 290                return -ENOMEM;
 291
 292        syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
 293        if (!syncpts)
 294                return -ENOMEM;
 295
 296        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 297        if (!regs) {
 298                dev_err(&pdev->dev, "failed to get registers\n");
 299                return -ENXIO;
 300        }
 301
 302        vic->regs = devm_ioremap_resource(dev, regs);
 303        if (IS_ERR(vic->regs))
 304                return PTR_ERR(vic->regs);
 305
 306        vic->clk = devm_clk_get(dev, NULL);
 307        if (IS_ERR(vic->clk)) {
 308                dev_err(&pdev->dev, "failed to get clock\n");
 309                return PTR_ERR(vic->clk);
 310        }
 311
 312        vic->falcon.dev = dev;
 313        vic->falcon.regs = vic->regs;
 314        vic->falcon.ops = &vic_falcon_ops;
 315
 316        err = falcon_init(&vic->falcon);
 317        if (err < 0)
 318                return err;
 319
 320        err = falcon_read_firmware(&vic->falcon, vic_config->firmware);
 321        if (err < 0)
 322                goto exit_falcon;
 323
 324        platform_set_drvdata(pdev, vic);
 325
 326        INIT_LIST_HEAD(&vic->client.base.list);
 327        vic->client.base.ops = &vic_client_ops;
 328        vic->client.base.dev = dev;
 329        vic->client.base.class = HOST1X_CLASS_VIC;
 330        vic->client.base.syncpts = syncpts;
 331        vic->client.base.num_syncpts = 1;
 332        vic->dev = dev;
 333        vic->config = vic_config;
 334
 335        INIT_LIST_HEAD(&vic->client.list);
 336        vic->client.ops = &vic_ops;
 337
 338        err = host1x_client_register(&vic->client.base);
 339        if (err < 0) {
 340                dev_err(dev, "failed to register host1x client: %d\n", err);
 341                platform_set_drvdata(pdev, NULL);
 342                goto exit_falcon;
 343        }
 344
 345        pm_runtime_enable(&pdev->dev);
 346        if (!pm_runtime_enabled(&pdev->dev)) {
 347                err = vic_runtime_resume(&pdev->dev);
 348                if (err < 0)
 349                        goto unregister_client;
 350        }
 351
 352        return 0;
 353
 354unregister_client:
 355        host1x_client_unregister(&vic->client.base);
 356exit_falcon:
 357        falcon_exit(&vic->falcon);
 358
 359        return err;
 360}
 361
 362static int vic_remove(struct platform_device *pdev)
 363{
 364        struct vic *vic = platform_get_drvdata(pdev);
 365        int err;
 366
 367        err = host1x_client_unregister(&vic->client.base);
 368        if (err < 0) {
 369                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
 370                        err);
 371                return err;
 372        }
 373
 374        if (pm_runtime_enabled(&pdev->dev))
 375                pm_runtime_disable(&pdev->dev);
 376        else
 377                vic_runtime_suspend(&pdev->dev);
 378
 379        falcon_exit(&vic->falcon);
 380
 381        return 0;
 382}
 383
 384static const struct dev_pm_ops vic_pm_ops = {
 385        SET_RUNTIME_PM_OPS(vic_runtime_suspend, vic_runtime_resume, NULL)
 386};
 387
 388struct platform_driver tegra_vic_driver = {
 389        .driver = {
 390                .name = "tegra-vic",
 391                .of_match_table = vic_match,
 392                .pm = &vic_pm_ops
 393        },
 394        .probe = vic_probe,
 395        .remove = vic_remove,
 396};
 397