linux/drivers/gpu/drm/tegra/host1x.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Avionic Design GmbH
   3 * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/err.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/platform_device.h>
  15
  16#include "drm.h"
  17
  18struct host1x_drm_client {
  19        struct host1x_client *client;
  20        struct device_node *np;
  21        struct list_head list;
  22};
  23
  24static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np)
  25{
  26        struct host1x_drm_client *client;
  27
  28        client = kzalloc(sizeof(*client), GFP_KERNEL);
  29        if (!client)
  30                return -ENOMEM;
  31
  32        INIT_LIST_HEAD(&client->list);
  33        client->np = of_node_get(np);
  34
  35        list_add_tail(&client->list, &host1x->drm_clients);
  36
  37        return 0;
  38}
  39
  40static int host1x_activate_drm_client(struct host1x *host1x,
  41                                      struct host1x_drm_client *drm,
  42                                      struct host1x_client *client)
  43{
  44        mutex_lock(&host1x->drm_clients_lock);
  45        list_del_init(&drm->list);
  46        list_add_tail(&drm->list, &host1x->drm_active);
  47        drm->client = client;
  48        mutex_unlock(&host1x->drm_clients_lock);
  49
  50        return 0;
  51}
  52
  53static int host1x_remove_drm_client(struct host1x *host1x,
  54                                    struct host1x_drm_client *client)
  55{
  56        mutex_lock(&host1x->drm_clients_lock);
  57        list_del_init(&client->list);
  58        mutex_unlock(&host1x->drm_clients_lock);
  59
  60        of_node_put(client->np);
  61        kfree(client);
  62
  63        return 0;
  64}
  65
  66static int host1x_parse_dt(struct host1x *host1x)
  67{
  68        static const char * const compat[] = {
  69                "nvidia,tegra20-dc",
  70                "nvidia,tegra20-hdmi",
  71                "nvidia,tegra30-dc",
  72                "nvidia,tegra30-hdmi",
  73        };
  74        unsigned int i;
  75        int err;
  76
  77        for (i = 0; i < ARRAY_SIZE(compat); i++) {
  78                struct device_node *np;
  79
  80                for_each_child_of_node(host1x->dev->of_node, np) {
  81                        if (of_device_is_compatible(np, compat[i]) &&
  82                            of_device_is_available(np)) {
  83                                err = host1x_add_drm_client(host1x, np);
  84                                if (err < 0)
  85                                        return err;
  86                        }
  87                }
  88        }
  89
  90        return 0;
  91}
  92
  93static int tegra_host1x_probe(struct platform_device *pdev)
  94{
  95        struct host1x *host1x;
  96        struct resource *regs;
  97        int err;
  98
  99        host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
 100        if (!host1x)
 101                return -ENOMEM;
 102
 103        mutex_init(&host1x->drm_clients_lock);
 104        INIT_LIST_HEAD(&host1x->drm_clients);
 105        INIT_LIST_HEAD(&host1x->drm_active);
 106        mutex_init(&host1x->clients_lock);
 107        INIT_LIST_HEAD(&host1x->clients);
 108        host1x->dev = &pdev->dev;
 109
 110        err = host1x_parse_dt(host1x);
 111        if (err < 0) {
 112                dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
 113                return err;
 114        }
 115
 116        host1x->clk = devm_clk_get(&pdev->dev, NULL);
 117        if (IS_ERR(host1x->clk))
 118                return PTR_ERR(host1x->clk);
 119
 120        err = clk_prepare_enable(host1x->clk);
 121        if (err < 0)
 122                return err;
 123
 124        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 125        if (!regs) {
 126                err = -ENXIO;
 127                goto err;
 128        }
 129
 130        err = platform_get_irq(pdev, 0);
 131        if (err < 0)
 132                goto err;
 133
 134        host1x->syncpt = err;
 135
 136        err = platform_get_irq(pdev, 1);
 137        if (err < 0)
 138                goto err;
 139
 140        host1x->irq = err;
 141
 142        host1x->regs = devm_ioremap_resource(&pdev->dev, regs);
 143        if (IS_ERR(host1x->regs)) {
 144                err = PTR_ERR(host1x->regs);
 145                goto err;
 146        }
 147
 148        platform_set_drvdata(pdev, host1x);
 149
 150        return 0;
 151
 152err:
 153        clk_disable_unprepare(host1x->clk);
 154        return err;
 155}
 156
 157static int tegra_host1x_remove(struct platform_device *pdev)
 158{
 159        struct host1x *host1x = platform_get_drvdata(pdev);
 160
 161        clk_disable_unprepare(host1x->clk);
 162
 163        return 0;
 164}
 165
 166int host1x_drm_init(struct host1x *host1x, struct drm_device *drm)
 167{
 168        struct host1x_client *client;
 169
 170        mutex_lock(&host1x->clients_lock);
 171
 172        list_for_each_entry(client, &host1x->clients, list) {
 173                if (client->ops && client->ops->drm_init) {
 174                        int err = client->ops->drm_init(client, drm);
 175                        if (err < 0) {
 176                                dev_err(host1x->dev,
 177                                        "DRM setup failed for %s: %d\n",
 178                                        dev_name(client->dev), err);
 179                                return err;
 180                        }
 181                }
 182        }
 183
 184        mutex_unlock(&host1x->clients_lock);
 185
 186        return 0;
 187}
 188
 189int host1x_drm_exit(struct host1x *host1x)
 190{
 191        struct platform_device *pdev = to_platform_device(host1x->dev);
 192        struct host1x_client *client;
 193
 194        if (!host1x->drm)
 195                return 0;
 196
 197        mutex_lock(&host1x->clients_lock);
 198
 199        list_for_each_entry_reverse(client, &host1x->clients, list) {
 200                if (client->ops && client->ops->drm_exit) {
 201                        int err = client->ops->drm_exit(client);
 202                        if (err < 0) {
 203                                dev_err(host1x->dev,
 204                                        "DRM cleanup failed for %s: %d\n",
 205                                        dev_name(client->dev), err);
 206                                return err;
 207                        }
 208                }
 209        }
 210
 211        mutex_unlock(&host1x->clients_lock);
 212
 213        drm_platform_exit(&tegra_drm_driver, pdev);
 214        host1x->drm = NULL;
 215
 216        return 0;
 217}
 218
 219int host1x_register_client(struct host1x *host1x, struct host1x_client *client)
 220{
 221        struct host1x_drm_client *drm, *tmp;
 222        int err;
 223
 224        mutex_lock(&host1x->clients_lock);
 225        list_add_tail(&client->list, &host1x->clients);
 226        mutex_unlock(&host1x->clients_lock);
 227
 228        list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
 229                if (drm->np == client->dev->of_node)
 230                        host1x_activate_drm_client(host1x, drm, client);
 231
 232        if (list_empty(&host1x->drm_clients)) {
 233                struct platform_device *pdev = to_platform_device(host1x->dev);
 234
 235                err = drm_platform_init(&tegra_drm_driver, pdev);
 236                if (err < 0) {
 237                        dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
 238                        return err;
 239                }
 240        }
 241
 242        client->host1x = host1x;
 243
 244        return 0;
 245}
 246
 247int host1x_unregister_client(struct host1x *host1x,
 248                             struct host1x_client *client)
 249{
 250        struct host1x_drm_client *drm, *tmp;
 251        int err;
 252
 253        list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
 254                if (drm->client == client) {
 255                        err = host1x_drm_exit(host1x);
 256                        if (err < 0) {
 257                                dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
 258                                        err);
 259                                return err;
 260                        }
 261
 262                        host1x_remove_drm_client(host1x, drm);
 263                        break;
 264                }
 265        }
 266
 267        mutex_lock(&host1x->clients_lock);
 268        list_del_init(&client->list);
 269        mutex_unlock(&host1x->clients_lock);
 270
 271        return 0;
 272}
 273
 274static struct of_device_id tegra_host1x_of_match[] = {
 275        { .compatible = "nvidia,tegra30-host1x", },
 276        { .compatible = "nvidia,tegra20-host1x", },
 277        { },
 278};
 279MODULE_DEVICE_TABLE(of, tegra_host1x_of_match);
 280
 281struct platform_driver tegra_host1x_driver = {
 282        .driver = {
 283                .name = "tegra-host1x",
 284                .owner = THIS_MODULE,
 285                .of_match_table = tegra_host1x_of_match,
 286        },
 287        .probe = tegra_host1x_probe,
 288        .remove = tegra_host1x_remove,
 289};
 290
 291static int __init tegra_host1x_init(void)
 292{
 293        int err;
 294
 295        err = platform_driver_register(&tegra_host1x_driver);
 296        if (err < 0)
 297                return err;
 298
 299        err = platform_driver_register(&tegra_dc_driver);
 300        if (err < 0)
 301                goto unregister_host1x;
 302
 303        err = platform_driver_register(&tegra_hdmi_driver);
 304        if (err < 0)
 305                goto unregister_dc;
 306
 307        return 0;
 308
 309unregister_dc:
 310        platform_driver_unregister(&tegra_dc_driver);
 311unregister_host1x:
 312        platform_driver_unregister(&tegra_host1x_driver);
 313        return err;
 314}
 315module_init(tegra_host1x_init);
 316
 317static void __exit tegra_host1x_exit(void)
 318{
 319        platform_driver_unregister(&tegra_hdmi_driver);
 320        platform_driver_unregister(&tegra_dc_driver);
 321        platform_driver_unregister(&tegra_host1x_driver);
 322}
 323module_exit(tegra_host1x_exit);
 324
 325MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
 326MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
 327MODULE_LICENSE("GPL");
 328