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