linux/drivers/gpu/host1x/drm/gr2d.c
<<
>>
Prefs
   1/*
   2 * drivers/video/tegra/host/gr2d/gr2d.c
   3 *
   4 * Tegra Graphics 2D
   5 *
   6 * Copyright (c) 2012-2013, NVIDIA Corporation.
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms and conditions of the GNU General Public License,
  10 * version 2, as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include <linux/export.h>
  22#include <linux/of.h>
  23#include <linux/of_device.h>
  24#include <linux/clk.h>
  25
  26#include "channel.h"
  27#include "drm.h"
  28#include "gem.h"
  29#include "job.h"
  30#include "host1x.h"
  31#include "host1x_bo.h"
  32#include "host1x_client.h"
  33#include "syncpt.h"
  34
  35struct gr2d {
  36        struct host1x_client client;
  37        struct clk *clk;
  38        struct host1x_channel *channel;
  39        unsigned long *addr_regs;
  40};
  41
  42static inline struct gr2d *to_gr2d(struct host1x_client *client)
  43{
  44        return container_of(client, struct gr2d, client);
  45}
  46
  47static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg);
  48
  49static int gr2d_client_init(struct host1x_client *client,
  50                            struct drm_device *drm)
  51{
  52        return 0;
  53}
  54
  55static int gr2d_client_exit(struct host1x_client *client)
  56{
  57        return 0;
  58}
  59
  60static int gr2d_open_channel(struct host1x_client *client,
  61                             struct host1x_drm_context *context)
  62{
  63        struct gr2d *gr2d = to_gr2d(client);
  64
  65        context->channel = host1x_channel_get(gr2d->channel);
  66
  67        if (!context->channel)
  68                return -ENOMEM;
  69
  70        return 0;
  71}
  72
  73static void gr2d_close_channel(struct host1x_drm_context *context)
  74{
  75        host1x_channel_put(context->channel);
  76}
  77
  78static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm,
  79                                          struct drm_file *file,
  80                                          u32 handle)
  81{
  82        struct drm_gem_object *gem;
  83        struct tegra_bo *bo;
  84
  85        gem = drm_gem_object_lookup(drm, file, handle);
  86        if (!gem)
  87                return 0;
  88
  89        mutex_lock(&drm->struct_mutex);
  90        drm_gem_object_unreference(gem);
  91        mutex_unlock(&drm->struct_mutex);
  92
  93        bo = to_tegra_bo(gem);
  94        return &bo->base;
  95}
  96
  97static int gr2d_submit(struct host1x_drm_context *context,
  98                       struct drm_tegra_submit *args, struct drm_device *drm,
  99                       struct drm_file *file)
 100{
 101        struct host1x_job *job;
 102        unsigned int num_cmdbufs = args->num_cmdbufs;
 103        unsigned int num_relocs = args->num_relocs;
 104        unsigned int num_waitchks = args->num_waitchks;
 105        struct drm_tegra_cmdbuf __user *cmdbufs =
 106                (void * __user)(uintptr_t)args->cmdbufs;
 107        struct drm_tegra_reloc __user *relocs =
 108                (void * __user)(uintptr_t)args->relocs;
 109        struct drm_tegra_waitchk __user *waitchks =
 110                (void * __user)(uintptr_t)args->waitchks;
 111        struct drm_tegra_syncpt syncpt;
 112        int err;
 113
 114        /* We don't yet support other than one syncpt_incr struct per submit */
 115        if (args->num_syncpts != 1)
 116                return -EINVAL;
 117
 118        job = host1x_job_alloc(context->channel, args->num_cmdbufs,
 119                               args->num_relocs, args->num_waitchks);
 120        if (!job)
 121                return -ENOMEM;
 122
 123        job->num_relocs = args->num_relocs;
 124        job->num_waitchk = args->num_waitchks;
 125        job->client = (u32)args->context;
 126        job->class = context->client->class;
 127        job->serialize = true;
 128
 129        while (num_cmdbufs) {
 130                struct drm_tegra_cmdbuf cmdbuf;
 131                struct host1x_bo *bo;
 132
 133                err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
 134                if (err)
 135                        goto fail;
 136
 137                bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
 138                if (!bo)
 139                        goto fail;
 140
 141                host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
 142                num_cmdbufs--;
 143                cmdbufs++;
 144        }
 145
 146        err = copy_from_user(job->relocarray, relocs,
 147                             sizeof(*relocs) * num_relocs);
 148        if (err)
 149                goto fail;
 150
 151        while (num_relocs--) {
 152                struct host1x_reloc *reloc = &job->relocarray[num_relocs];
 153                struct host1x_bo *cmdbuf, *target;
 154
 155                cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
 156                target = host1x_bo_lookup(drm, file, (u32)reloc->target);
 157
 158                reloc->cmdbuf = cmdbuf;
 159                reloc->target = target;
 160
 161                if (!reloc->target || !reloc->cmdbuf)
 162                        goto fail;
 163        }
 164
 165        err = copy_from_user(job->waitchk, waitchks,
 166                             sizeof(*waitchks) * num_waitchks);
 167        if (err)
 168                goto fail;
 169
 170        err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
 171                             sizeof(syncpt));
 172        if (err)
 173                goto fail;
 174
 175        job->syncpt_id = syncpt.id;
 176        job->syncpt_incrs = syncpt.incrs;
 177        job->timeout = 10000;
 178        job->is_addr_reg = gr2d_is_addr_reg;
 179
 180        if (args->timeout && args->timeout < 10000)
 181                job->timeout = args->timeout;
 182
 183        err = host1x_job_pin(job, context->client->dev);
 184        if (err)
 185                goto fail;
 186
 187        err = host1x_job_submit(job);
 188        if (err)
 189                goto fail_submit;
 190
 191        args->fence = job->syncpt_end;
 192
 193        host1x_job_put(job);
 194        return 0;
 195
 196fail_submit:
 197        host1x_job_unpin(job);
 198fail:
 199        host1x_job_put(job);
 200        return err;
 201}
 202
 203static struct host1x_client_ops gr2d_client_ops = {
 204        .drm_init = gr2d_client_init,
 205        .drm_exit = gr2d_client_exit,
 206        .open_channel = gr2d_open_channel,
 207        .close_channel = gr2d_close_channel,
 208        .submit = gr2d_submit,
 209};
 210
 211static void gr2d_init_addr_reg_map(struct device *dev, struct gr2d *gr2d)
 212{
 213        const u32 gr2d_addr_regs[] = {0x1a, 0x1b, 0x26, 0x2b, 0x2c, 0x2d, 0x31,
 214                                      0x32, 0x48, 0x49, 0x4a, 0x4b, 0x4c};
 215        unsigned long *bitmap;
 216        int i;
 217
 218        bitmap = devm_kzalloc(dev, DIV_ROUND_UP(256, BITS_PER_BYTE),
 219                              GFP_KERNEL);
 220
 221        for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); ++i) {
 222                u32 reg = gr2d_addr_regs[i];
 223                bitmap[BIT_WORD(reg)] |= BIT_MASK(reg);
 224        }
 225
 226        gr2d->addr_regs = bitmap;
 227}
 228
 229static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg)
 230{
 231        struct gr2d *gr2d = dev_get_drvdata(dev);
 232
 233        switch (class) {
 234        case HOST1X_CLASS_HOST1X:
 235                return reg == 0x2b;
 236        case HOST1X_CLASS_GR2D:
 237        case HOST1X_CLASS_GR2D_SB:
 238                reg &= 0xff;
 239                if (gr2d->addr_regs[BIT_WORD(reg)] & BIT_MASK(reg))
 240                        return 1;
 241        default:
 242                return 0;
 243        }
 244}
 245
 246static const struct of_device_id gr2d_match[] = {
 247        { .compatible = "nvidia,tegra30-gr2d" },
 248        { .compatible = "nvidia,tegra20-gr2d" },
 249        { },
 250};
 251
 252static int gr2d_probe(struct platform_device *pdev)
 253{
 254        struct device *dev = &pdev->dev;
 255        struct host1x_drm *host1x = host1x_get_drm_data(dev->parent);
 256        int err;
 257        struct gr2d *gr2d = NULL;
 258        struct host1x_syncpt **syncpts;
 259
 260        gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
 261        if (!gr2d)
 262                return -ENOMEM;
 263
 264        syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
 265        if (!syncpts)
 266                return -ENOMEM;
 267
 268        gr2d->clk = devm_clk_get(dev, NULL);
 269        if (IS_ERR(gr2d->clk)) {
 270                dev_err(dev, "cannot get clock\n");
 271                return PTR_ERR(gr2d->clk);
 272        }
 273
 274        err = clk_prepare_enable(gr2d->clk);
 275        if (err) {
 276                dev_err(dev, "cannot turn on clock\n");
 277                return err;
 278        }
 279
 280        gr2d->channel = host1x_channel_request(dev);
 281        if (!gr2d->channel)
 282                return -ENOMEM;
 283
 284        *syncpts = host1x_syncpt_request(dev, 0);
 285        if (!(*syncpts)) {
 286                host1x_channel_free(gr2d->channel);
 287                return -ENOMEM;
 288        }
 289
 290        gr2d->client.ops = &gr2d_client_ops;
 291        gr2d->client.dev = dev;
 292        gr2d->client.class = HOST1X_CLASS_GR2D;
 293        gr2d->client.syncpts = syncpts;
 294        gr2d->client.num_syncpts = 1;
 295
 296        err = host1x_register_client(host1x, &gr2d->client);
 297        if (err < 0) {
 298                dev_err(dev, "failed to register host1x client: %d\n", err);
 299                return err;
 300        }
 301
 302        gr2d_init_addr_reg_map(dev, gr2d);
 303
 304        platform_set_drvdata(pdev, gr2d);
 305
 306        return 0;
 307}
 308
 309static int __exit gr2d_remove(struct platform_device *pdev)
 310{
 311        struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
 312        struct gr2d *gr2d = platform_get_drvdata(pdev);
 313        unsigned int i;
 314        int err;
 315
 316        err = host1x_unregister_client(host1x, &gr2d->client);
 317        if (err < 0) {
 318                dev_err(&pdev->dev, "failed to unregister client: %d\n", err);
 319                return err;
 320        }
 321
 322        for (i = 0; i < gr2d->client.num_syncpts; i++)
 323                host1x_syncpt_free(gr2d->client.syncpts[i]);
 324
 325        host1x_channel_free(gr2d->channel);
 326        clk_disable_unprepare(gr2d->clk);
 327
 328        return 0;
 329}
 330
 331struct platform_driver tegra_gr2d_driver = {
 332        .probe = gr2d_probe,
 333        .remove = __exit_p(gr2d_remove),
 334        .driver = {
 335                .owner = THIS_MODULE,
 336                .name = "gr2d",
 337                .of_match_table = gr2d_match,
 338        }
 339};
 340