linux/drivers/gpu/drm/tegra/gr2d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2012-2013, NVIDIA Corporation.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/iommu.h>
   8#include <linux/module.h>
   9#include <linux/of_device.h>
  10
  11#include "drm.h"
  12#include "gem.h"
  13#include "gr2d.h"
  14
  15struct gr2d_soc {
  16        unsigned int version;
  17};
  18
  19struct gr2d {
  20        struct tegra_drm_client client;
  21        struct host1x_channel *channel;
  22        struct clk *clk;
  23
  24        const struct gr2d_soc *soc;
  25
  26        DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
  27};
  28
  29static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
  30{
  31        return container_of(client, struct gr2d, client);
  32}
  33
  34static int gr2d_init(struct host1x_client *client)
  35{
  36        struct tegra_drm_client *drm = host1x_to_drm_client(client);
  37        struct drm_device *dev = dev_get_drvdata(client->host);
  38        unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
  39        struct gr2d *gr2d = to_gr2d(drm);
  40        int err;
  41
  42        gr2d->channel = host1x_channel_request(client);
  43        if (!gr2d->channel)
  44                return -ENOMEM;
  45
  46        client->syncpts[0] = host1x_syncpt_request(client, flags);
  47        if (!client->syncpts[0]) {
  48                err = -ENOMEM;
  49                dev_err(client->dev, "failed to request syncpoint: %d\n", err);
  50                goto put;
  51        }
  52
  53        err = host1x_client_iommu_attach(client);
  54        if (err < 0) {
  55                dev_err(client->dev, "failed to attach to domain: %d\n", err);
  56                goto free;
  57        }
  58
  59        err = tegra_drm_register_client(dev->dev_private, drm);
  60        if (err < 0) {
  61                dev_err(client->dev, "failed to register client: %d\n", err);
  62                goto detach;
  63        }
  64
  65        return 0;
  66
  67detach:
  68        host1x_client_iommu_detach(client);
  69free:
  70        host1x_syncpt_put(client->syncpts[0]);
  71put:
  72        host1x_channel_put(gr2d->channel);
  73        return err;
  74}
  75
  76static int gr2d_exit(struct host1x_client *client)
  77{
  78        struct tegra_drm_client *drm = host1x_to_drm_client(client);
  79        struct drm_device *dev = dev_get_drvdata(client->host);
  80        struct tegra_drm *tegra = dev->dev_private;
  81        struct gr2d *gr2d = to_gr2d(drm);
  82        int err;
  83
  84        err = tegra_drm_unregister_client(tegra, drm);
  85        if (err < 0)
  86                return err;
  87
  88        host1x_client_iommu_detach(client);
  89        host1x_syncpt_put(client->syncpts[0]);
  90        host1x_channel_put(gr2d->channel);
  91
  92        return 0;
  93}
  94
  95static const struct host1x_client_ops gr2d_client_ops = {
  96        .init = gr2d_init,
  97        .exit = gr2d_exit,
  98};
  99
 100static int gr2d_open_channel(struct tegra_drm_client *client,
 101                             struct tegra_drm_context *context)
 102{
 103        struct gr2d *gr2d = to_gr2d(client);
 104
 105        context->channel = host1x_channel_get(gr2d->channel);
 106        if (!context->channel)
 107                return -ENOMEM;
 108
 109        return 0;
 110}
 111
 112static void gr2d_close_channel(struct tegra_drm_context *context)
 113{
 114        host1x_channel_put(context->channel);
 115}
 116
 117static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset)
 118{
 119        struct gr2d *gr2d = dev_get_drvdata(dev);
 120
 121        switch (class) {
 122        case HOST1X_CLASS_HOST1X:
 123                if (offset == 0x2b)
 124                        return 1;
 125
 126                break;
 127
 128        case HOST1X_CLASS_GR2D:
 129        case HOST1X_CLASS_GR2D_SB:
 130                if (offset >= GR2D_NUM_REGS)
 131                        break;
 132
 133                if (test_bit(offset, gr2d->addr_regs))
 134                        return 1;
 135
 136                break;
 137        }
 138
 139        return 0;
 140}
 141
 142static int gr2d_is_valid_class(u32 class)
 143{
 144        return (class == HOST1X_CLASS_GR2D ||
 145                class == HOST1X_CLASS_GR2D_SB);
 146}
 147
 148static const struct tegra_drm_client_ops gr2d_ops = {
 149        .open_channel = gr2d_open_channel,
 150        .close_channel = gr2d_close_channel,
 151        .is_addr_reg = gr2d_is_addr_reg,
 152        .is_valid_class = gr2d_is_valid_class,
 153        .submit = tegra_drm_submit,
 154};
 155
 156static const struct gr2d_soc tegra20_gr2d_soc = {
 157        .version = 0x20,
 158};
 159
 160static const struct gr2d_soc tegra30_gr2d_soc = {
 161        .version = 0x30,
 162};
 163
 164static const struct gr2d_soc tegra114_gr2d_soc = {
 165        .version = 0x35,
 166};
 167
 168static const struct of_device_id gr2d_match[] = {
 169        { .compatible = "nvidia,tegra114-gr2d", .data = &tegra114_gr2d_soc },
 170        { .compatible = "nvidia,tegra30-gr2d", .data = &tegra30_gr2d_soc },
 171        { .compatible = "nvidia,tegra20-gr2d", .data = &tegra20_gr2d_soc },
 172        { },
 173};
 174MODULE_DEVICE_TABLE(of, gr2d_match);
 175
 176static const u32 gr2d_addr_regs[] = {
 177        GR2D_UA_BASE_ADDR,
 178        GR2D_VA_BASE_ADDR,
 179        GR2D_PAT_BASE_ADDR,
 180        GR2D_DSTA_BASE_ADDR,
 181        GR2D_DSTB_BASE_ADDR,
 182        GR2D_DSTC_BASE_ADDR,
 183        GR2D_SRCA_BASE_ADDR,
 184        GR2D_SRCB_BASE_ADDR,
 185        GR2D_PATBASE_ADDR,
 186        GR2D_SRC_BASE_ADDR_SB,
 187        GR2D_DSTA_BASE_ADDR_SB,
 188        GR2D_DSTB_BASE_ADDR_SB,
 189        GR2D_UA_BASE_ADDR_SB,
 190        GR2D_VA_BASE_ADDR_SB,
 191};
 192
 193static int gr2d_probe(struct platform_device *pdev)
 194{
 195        struct device *dev = &pdev->dev;
 196        struct host1x_syncpt **syncpts;
 197        struct gr2d *gr2d;
 198        unsigned int i;
 199        int err;
 200
 201        gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
 202        if (!gr2d)
 203                return -ENOMEM;
 204
 205        gr2d->soc = of_device_get_match_data(dev);
 206
 207        syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
 208        if (!syncpts)
 209                return -ENOMEM;
 210
 211        gr2d->clk = devm_clk_get(dev, NULL);
 212        if (IS_ERR(gr2d->clk)) {
 213                dev_err(dev, "cannot get clock\n");
 214                return PTR_ERR(gr2d->clk);
 215        }
 216
 217        err = clk_prepare_enable(gr2d->clk);
 218        if (err) {
 219                dev_err(dev, "cannot turn on clock\n");
 220                return err;
 221        }
 222
 223        INIT_LIST_HEAD(&gr2d->client.base.list);
 224        gr2d->client.base.ops = &gr2d_client_ops;
 225        gr2d->client.base.dev = dev;
 226        gr2d->client.base.class = HOST1X_CLASS_GR2D;
 227        gr2d->client.base.syncpts = syncpts;
 228        gr2d->client.base.num_syncpts = 1;
 229
 230        INIT_LIST_HEAD(&gr2d->client.list);
 231        gr2d->client.version = gr2d->soc->version;
 232        gr2d->client.ops = &gr2d_ops;
 233
 234        err = host1x_client_register(&gr2d->client.base);
 235        if (err < 0) {
 236                dev_err(dev, "failed to register host1x client: %d\n", err);
 237                clk_disable_unprepare(gr2d->clk);
 238                return err;
 239        }
 240
 241        /* initialize address register map */
 242        for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
 243                set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
 244
 245        platform_set_drvdata(pdev, gr2d);
 246
 247        return 0;
 248}
 249
 250static int gr2d_remove(struct platform_device *pdev)
 251{
 252        struct gr2d *gr2d = platform_get_drvdata(pdev);
 253        int err;
 254
 255        err = host1x_client_unregister(&gr2d->client.base);
 256        if (err < 0) {
 257                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
 258                        err);
 259                return err;
 260        }
 261
 262        clk_disable_unprepare(gr2d->clk);
 263
 264        return 0;
 265}
 266
 267struct platform_driver tegra_gr2d_driver = {
 268        .driver = {
 269                .name = "tegra-gr2d",
 270                .of_match_table = gr2d_match,
 271        },
 272        .probe = gr2d_probe,
 273        .remove = gr2d_remove,
 274};
 275