linux/drivers/gpu/drm/armada/armada_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Russell King
   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#include <linux/clk.h>
   9#include <linux/component.h>
  10#include <linux/module.h>
  11#include <linux/of_graph.h>
  12#include <drm/drmP.h>
  13#include <drm/drm_crtc_helper.h>
  14#include <drm/drm_of.h>
  15#include "armada_crtc.h"
  16#include "armada_drm.h"
  17#include "armada_gem.h"
  18#include "armada_hw.h"
  19#include <drm/armada_drm.h>
  20#include "armada_ioctlP.h"
  21
  22static void armada_drm_unref_work(struct work_struct *work)
  23{
  24        struct armada_private *priv =
  25                container_of(work, struct armada_private, fb_unref_work);
  26        struct drm_framebuffer *fb;
  27
  28        while (kfifo_get(&priv->fb_unref, &fb))
  29                drm_framebuffer_unreference(fb);
  30}
  31
  32/* Must be called with dev->event_lock held */
  33void __armada_drm_queue_unref_work(struct drm_device *dev,
  34        struct drm_framebuffer *fb)
  35{
  36        struct armada_private *priv = dev->dev_private;
  37
  38        WARN_ON(!kfifo_put(&priv->fb_unref, fb));
  39        schedule_work(&priv->fb_unref_work);
  40}
  41
  42void armada_drm_queue_unref_work(struct drm_device *dev,
  43        struct drm_framebuffer *fb)
  44{
  45        unsigned long flags;
  46
  47        spin_lock_irqsave(&dev->event_lock, flags);
  48        __armada_drm_queue_unref_work(dev, fb);
  49        spin_unlock_irqrestore(&dev->event_lock, flags);
  50}
  51
  52static struct drm_ioctl_desc armada_ioctls[] = {
  53        DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,0),
  54        DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl, 0),
  55        DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl, 0),
  56};
  57
  58static void armada_drm_lastclose(struct drm_device *dev)
  59{
  60        armada_fbdev_lastclose(dev);
  61}
  62
  63DEFINE_DRM_GEM_FOPS(armada_drm_fops);
  64
  65static struct drm_driver armada_drm_driver = {
  66        .lastclose              = armada_drm_lastclose,
  67        .gem_free_object_unlocked = armada_gem_free_object,
  68        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
  69        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
  70        .gem_prime_export       = armada_gem_prime_export,
  71        .gem_prime_import       = armada_gem_prime_import,
  72        .dumb_create            = armada_gem_dumb_create,
  73        .dumb_map_offset        = armada_gem_dumb_map_offset,
  74        .dumb_destroy           = armada_gem_dumb_destroy,
  75        .gem_vm_ops             = &armada_gem_vm_ops,
  76        .major                  = 1,
  77        .minor                  = 0,
  78        .name                   = "armada-drm",
  79        .desc                   = "Armada SoC DRM",
  80        .date                   = "20120730",
  81        .driver_features        = DRIVER_GEM | DRIVER_MODESET |
  82                                  DRIVER_PRIME,
  83        .ioctls                 = armada_ioctls,
  84        .fops                   = &armada_drm_fops,
  85};
  86
  87static int armada_drm_bind(struct device *dev)
  88{
  89        struct armada_private *priv;
  90        struct resource *mem = NULL;
  91        int ret, n;
  92
  93        for (n = 0; ; n++) {
  94                struct resource *r = platform_get_resource(to_platform_device(dev),
  95                                                           IORESOURCE_MEM, n);
  96                if (!r)
  97                        break;
  98
  99                /* Resources above 64K are graphics memory */
 100                if (resource_size(r) > SZ_64K)
 101                        mem = r;
 102                else
 103                        return -EINVAL;
 104        }
 105
 106        if (!mem)
 107                return -ENXIO;
 108
 109        if (!devm_request_mem_region(dev, mem->start, resource_size(mem),
 110                                     "armada-drm"))
 111                return -EBUSY;
 112
 113        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 114        if (!priv)
 115                return -ENOMEM;
 116
 117        /*
 118         * The drm_device structure must be at the start of
 119         * armada_private for drm_dev_unref() to work correctly.
 120         */
 121        BUILD_BUG_ON(offsetof(struct armada_private, drm) != 0);
 122
 123        ret = drm_dev_init(&priv->drm, &armada_drm_driver, dev);
 124        if (ret) {
 125                dev_err(dev, "[" DRM_NAME ":%s] drm_dev_init failed: %d\n",
 126                        __func__, ret);
 127                kfree(priv);
 128                return ret;
 129        }
 130
 131        priv->drm.dev_private = priv;
 132
 133        dev_set_drvdata(dev, &priv->drm);
 134
 135        INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
 136        INIT_KFIFO(priv->fb_unref);
 137
 138        /* Mode setting support */
 139        drm_mode_config_init(&priv->drm);
 140        priv->drm.mode_config.min_width = 320;
 141        priv->drm.mode_config.min_height = 200;
 142
 143        /*
 144         * With vscale enabled, the maximum width is 1920 due to the
 145         * 1920 by 3 lines RAM
 146         */
 147        priv->drm.mode_config.max_width = 1920;
 148        priv->drm.mode_config.max_height = 2048;
 149
 150        priv->drm.mode_config.preferred_depth = 24;
 151        priv->drm.mode_config.funcs = &armada_drm_mode_config_funcs;
 152        drm_mm_init(&priv->linear, mem->start, resource_size(mem));
 153        mutex_init(&priv->linear_lock);
 154
 155        ret = component_bind_all(dev, &priv->drm);
 156        if (ret)
 157                goto err_kms;
 158
 159        ret = drm_vblank_init(&priv->drm, priv->drm.mode_config.num_crtc);
 160        if (ret)
 161                goto err_comp;
 162
 163        priv->drm.irq_enabled = true;
 164
 165        ret = armada_fbdev_init(&priv->drm);
 166        if (ret)
 167                goto err_comp;
 168
 169        drm_kms_helper_poll_init(&priv->drm);
 170
 171        ret = drm_dev_register(&priv->drm, 0);
 172        if (ret)
 173                goto err_poll;
 174
 175#ifdef CONFIG_DEBUG_FS
 176        armada_drm_debugfs_init(priv->drm.primary);
 177#endif
 178
 179        return 0;
 180
 181 err_poll:
 182        drm_kms_helper_poll_fini(&priv->drm);
 183        armada_fbdev_fini(&priv->drm);
 184 err_comp:
 185        component_unbind_all(dev, &priv->drm);
 186 err_kms:
 187        drm_mode_config_cleanup(&priv->drm);
 188        drm_mm_takedown(&priv->linear);
 189        flush_work(&priv->fb_unref_work);
 190        drm_dev_unref(&priv->drm);
 191        return ret;
 192}
 193
 194static void armada_drm_unbind(struct device *dev)
 195{
 196        struct drm_device *drm = dev_get_drvdata(dev);
 197        struct armada_private *priv = drm->dev_private;
 198
 199        drm_kms_helper_poll_fini(&priv->drm);
 200        armada_fbdev_fini(&priv->drm);
 201
 202        drm_dev_unregister(&priv->drm);
 203
 204        component_unbind_all(dev, &priv->drm);
 205
 206        drm_mode_config_cleanup(&priv->drm);
 207        drm_mm_takedown(&priv->linear);
 208        flush_work(&priv->fb_unref_work);
 209
 210        drm_dev_unref(&priv->drm);
 211}
 212
 213static int compare_of(struct device *dev, void *data)
 214{
 215        return dev->of_node == data;
 216}
 217
 218static int compare_dev_name(struct device *dev, void *data)
 219{
 220        const char *name = data;
 221        return !strcmp(dev_name(dev), name);
 222}
 223
 224static void armada_add_endpoints(struct device *dev,
 225        struct component_match **match, struct device_node *port)
 226{
 227        struct device_node *ep, *remote;
 228
 229        for_each_child_of_node(port, ep) {
 230                remote = of_graph_get_remote_port_parent(ep);
 231                if (!remote || !of_device_is_available(remote)) {
 232                        of_node_put(remote);
 233                        continue;
 234                } else if (!of_device_is_available(remote->parent)) {
 235                        dev_warn(dev, "parent device of %pOF is not available\n",
 236                                 remote);
 237                        of_node_put(remote);
 238                        continue;
 239                }
 240
 241                drm_of_component_match_add(dev, match, compare_of, remote);
 242                of_node_put(remote);
 243        }
 244}
 245
 246static const struct component_master_ops armada_master_ops = {
 247        .bind = armada_drm_bind,
 248        .unbind = armada_drm_unbind,
 249};
 250
 251static int armada_drm_probe(struct platform_device *pdev)
 252{
 253        struct component_match *match = NULL;
 254        struct device *dev = &pdev->dev;
 255        int ret;
 256
 257        ret = drm_of_component_probe(dev, compare_dev_name, &armada_master_ops);
 258        if (ret != -EINVAL)
 259                return ret;
 260
 261        if (dev->platform_data) {
 262                char **devices = dev->platform_data;
 263                struct device_node *port;
 264                struct device *d;
 265                int i;
 266
 267                for (i = 0; devices[i]; i++)
 268                        component_match_add(dev, &match, compare_dev_name,
 269                                            devices[i]);
 270
 271                if (i == 0) {
 272                        dev_err(dev, "missing 'ports' property\n");
 273                        return -ENODEV;
 274                }
 275
 276                for (i = 0; devices[i]; i++) {
 277                        d = bus_find_device_by_name(&platform_bus_type, NULL,
 278                                                    devices[i]);
 279                        if (d && d->of_node) {
 280                                for_each_child_of_node(d->of_node, port)
 281                                        armada_add_endpoints(dev, &match, port);
 282                        }
 283                        put_device(d);
 284                }
 285        }
 286
 287        return component_master_add_with_match(&pdev->dev, &armada_master_ops,
 288                                               match);
 289}
 290
 291static int armada_drm_remove(struct platform_device *pdev)
 292{
 293        component_master_del(&pdev->dev, &armada_master_ops);
 294        return 0;
 295}
 296
 297static const struct platform_device_id armada_drm_platform_ids[] = {
 298        {
 299                .name           = "armada-drm",
 300        }, {
 301                .name           = "armada-510-drm",
 302        },
 303        { },
 304};
 305MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
 306
 307static struct platform_driver armada_drm_platform_driver = {
 308        .probe  = armada_drm_probe,
 309        .remove = armada_drm_remove,
 310        .driver = {
 311                .name   = "armada-drm",
 312        },
 313        .id_table = armada_drm_platform_ids,
 314};
 315
 316static int __init armada_drm_init(void)
 317{
 318        int ret;
 319
 320        armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
 321
 322        ret = platform_driver_register(&armada_lcd_platform_driver);
 323        if (ret)
 324                return ret;
 325        ret = platform_driver_register(&armada_drm_platform_driver);
 326        if (ret)
 327                platform_driver_unregister(&armada_lcd_platform_driver);
 328        return ret;
 329}
 330module_init(armada_drm_init);
 331
 332static void __exit armada_drm_exit(void)
 333{
 334        platform_driver_unregister(&armada_drm_platform_driver);
 335        platform_driver_unregister(&armada_lcd_platform_driver);
 336}
 337module_exit(armada_drm_exit);
 338
 339MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
 340MODULE_DESCRIPTION("Armada DRM Driver");
 341MODULE_LICENSE("GPL");
 342MODULE_ALIAS("platform:armada-drm");
 343