linux/drivers/gpu/drm/sun4i/sun4i_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Free Electrons
   3 * Copyright (C) 2015 NextThing Co
   4 *
   5 * Maxime Ripard <maxime.ripard@free-electrons.com>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 of
  10 * the License, or (at your option) any later version.
  11 */
  12
  13#include <linux/component.h>
  14#include <linux/of_graph.h>
  15
  16#include <drm/drmP.h>
  17#include <drm/drm_crtc_helper.h>
  18#include <drm/drm_fb_cma_helper.h>
  19#include <drm/drm_gem_cma_helper.h>
  20
  21#include "sun4i_crtc.h"
  22#include "sun4i_drv.h"
  23#include "sun4i_framebuffer.h"
  24#include "sun4i_layer.h"
  25#include "sun4i_tcon.h"
  26
  27static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
  28{
  29        struct sun4i_drv *drv = drm->dev_private;
  30        struct sun4i_tcon *tcon = drv->tcon;
  31
  32        DRM_DEBUG_DRIVER("Enabling VBLANK on pipe %d\n", pipe);
  33
  34        sun4i_tcon_enable_vblank(tcon, true);
  35
  36        return 0;
  37}
  38
  39static void sun4i_drv_disable_vblank(struct drm_device *drm, unsigned int pipe)
  40{
  41        struct sun4i_drv *drv = drm->dev_private;
  42        struct sun4i_tcon *tcon = drv->tcon;
  43
  44        DRM_DEBUG_DRIVER("Disabling VBLANK on pipe %d\n", pipe);
  45
  46        sun4i_tcon_enable_vblank(tcon, false);
  47}
  48
  49static const struct file_operations sun4i_drv_fops = {
  50        .owner          = THIS_MODULE,
  51        .open           = drm_open,
  52        .release        = drm_release,
  53        .unlocked_ioctl = drm_ioctl,
  54#ifdef CONFIG_COMPAT
  55        .compat_ioctl   = drm_compat_ioctl,
  56#endif
  57        .poll           = drm_poll,
  58        .read           = drm_read,
  59        .llseek         = no_llseek,
  60        .mmap           = drm_gem_cma_mmap,
  61};
  62
  63static struct drm_driver sun4i_drv_driver = {
  64        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
  65
  66        /* Generic Operations */
  67        .fops                   = &sun4i_drv_fops,
  68        .name                   = "sun4i-drm",
  69        .desc                   = "Allwinner sun4i Display Engine",
  70        .date                   = "20150629",
  71        .major                  = 1,
  72        .minor                  = 0,
  73
  74        /* GEM Operations */
  75        .dumb_create            = drm_gem_cma_dumb_create,
  76        .dumb_destroy           = drm_gem_dumb_destroy,
  77        .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
  78        .gem_free_object_unlocked = drm_gem_cma_free_object,
  79        .gem_vm_ops             = &drm_gem_cma_vm_ops,
  80
  81        /* PRIME Operations */
  82        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
  83        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
  84        .gem_prime_import       = drm_gem_prime_import,
  85        .gem_prime_export       = drm_gem_prime_export,
  86        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
  87        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
  88        .gem_prime_vmap         = drm_gem_cma_prime_vmap,
  89        .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
  90        .gem_prime_mmap         = drm_gem_cma_prime_mmap,
  91
  92        /* Frame Buffer Operations */
  93
  94        /* VBlank Operations */
  95        .get_vblank_counter     = drm_vblank_no_hw_counter,
  96        .enable_vblank          = sun4i_drv_enable_vblank,
  97        .disable_vblank         = sun4i_drv_disable_vblank,
  98};
  99
 100static void sun4i_remove_framebuffers(void)
 101{
 102        struct apertures_struct *ap;
 103
 104        ap = alloc_apertures(1);
 105        if (!ap)
 106                return;
 107
 108        /* The framebuffer can be located anywhere in RAM */
 109        ap->ranges[0].base = 0;
 110        ap->ranges[0].size = ~0;
 111
 112        remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
 113        kfree(ap);
 114}
 115
 116static int sun4i_drv_bind(struct device *dev)
 117{
 118        struct drm_device *drm;
 119        struct sun4i_drv *drv;
 120        int ret;
 121
 122        drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 123        if (!drm)
 124                return -ENOMEM;
 125
 126        drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
 127        if (!drv) {
 128                ret = -ENOMEM;
 129                goto free_drm;
 130        }
 131        drm->dev_private = drv;
 132
 133        drm_vblank_init(drm, 1);
 134        drm_mode_config_init(drm);
 135
 136        ret = component_bind_all(drm->dev, drm);
 137        if (ret) {
 138                dev_err(drm->dev, "Couldn't bind all pipelines components\n");
 139                goto free_drm;
 140        }
 141
 142        /* Create our layers */
 143        drv->layers = sun4i_layers_init(drm);
 144        if (!drv->layers) {
 145                dev_err(drm->dev, "Couldn't create the planes\n");
 146                ret = -EINVAL;
 147                goto free_drm;
 148        }
 149
 150        /* Create our CRTC */
 151        drv->crtc = sun4i_crtc_init(drm);
 152        if (!drv->crtc) {
 153                dev_err(drm->dev, "Couldn't create the CRTC\n");
 154                ret = -EINVAL;
 155                goto free_drm;
 156        }
 157        drm->irq_enabled = true;
 158
 159        /* Remove early framebuffers (ie. simplefb) */
 160        sun4i_remove_framebuffers();
 161
 162        /* Create our framebuffer */
 163        drv->fbdev = sun4i_framebuffer_init(drm);
 164        if (IS_ERR(drv->fbdev)) {
 165                dev_err(drm->dev, "Couldn't create our framebuffer\n");
 166                ret = PTR_ERR(drv->fbdev);
 167                goto free_drm;
 168        }
 169
 170        /* Enable connectors polling */
 171        drm_kms_helper_poll_init(drm);
 172
 173        ret = drm_dev_register(drm, 0);
 174        if (ret)
 175                goto free_drm;
 176
 177        return 0;
 178
 179free_drm:
 180        drm_dev_unref(drm);
 181        return ret;
 182}
 183
 184static void sun4i_drv_unbind(struct device *dev)
 185{
 186        struct drm_device *drm = dev_get_drvdata(dev);
 187
 188        drm_dev_unregister(drm);
 189        drm_kms_helper_poll_fini(drm);
 190        sun4i_framebuffer_free(drm);
 191        drm_vblank_cleanup(drm);
 192        drm_dev_unref(drm);
 193}
 194
 195static const struct component_master_ops sun4i_drv_master_ops = {
 196        .bind   = sun4i_drv_bind,
 197        .unbind = sun4i_drv_unbind,
 198};
 199
 200static bool sun4i_drv_node_is_frontend(struct device_node *node)
 201{
 202        return of_device_is_compatible(node,
 203                                       "allwinner,sun5i-a13-display-frontend");
 204}
 205
 206static bool sun4i_drv_node_is_tcon(struct device_node *node)
 207{
 208        return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon");
 209}
 210
 211static int compare_of(struct device *dev, void *data)
 212{
 213        DRM_DEBUG_DRIVER("Comparing of node %s with %s\n",
 214                         of_node_full_name(dev->of_node),
 215                         of_node_full_name(data));
 216
 217        return dev->of_node == data;
 218}
 219
 220static int sun4i_drv_add_endpoints(struct device *dev,
 221                                   struct component_match **match,
 222                                   struct device_node *node)
 223{
 224        struct device_node *port, *ep, *remote;
 225        int count = 0;
 226
 227        /*
 228         * We don't support the frontend for now, so we will never
 229         * have a device bound. Just skip over it, but we still want
 230         * the rest our pipeline to be added.
 231         */
 232        if (!sun4i_drv_node_is_frontend(node) &&
 233            !of_device_is_available(node))
 234                return 0;
 235
 236        if (!sun4i_drv_node_is_frontend(node)) {
 237                /* Add current component */
 238                DRM_DEBUG_DRIVER("Adding component %s\n",
 239                                 of_node_full_name(node));
 240                component_match_add(dev, match, compare_of, node);
 241                count++;
 242        }
 243
 244        /* Inputs are listed first, then outputs */
 245        port = of_graph_get_port_by_id(node, 1);
 246        if (!port) {
 247                DRM_DEBUG_DRIVER("No output to bind\n");
 248                return count;
 249        }
 250
 251        for_each_available_child_of_node(port, ep) {
 252                remote = of_graph_get_remote_port_parent(ep);
 253                if (!remote) {
 254                        DRM_DEBUG_DRIVER("Error retrieving the output node\n");
 255                        of_node_put(remote);
 256                        continue;
 257                }
 258
 259                /*
 260                 * If the node is our TCON, the first port is used for our
 261                 * panel, and will not be part of the
 262                 * component framework.
 263                 */
 264                if (sun4i_drv_node_is_tcon(node)) {
 265                        struct of_endpoint endpoint;
 266
 267                        if (of_graph_parse_endpoint(ep, &endpoint)) {
 268                                DRM_DEBUG_DRIVER("Couldn't parse endpoint\n");
 269                                continue;
 270                        }
 271
 272                        if (!endpoint.id) {
 273                                DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n");
 274                                continue;
 275                        }
 276                }
 277
 278                /* Walk down our tree */
 279                count += sun4i_drv_add_endpoints(dev, match, remote);
 280
 281                of_node_put(remote);
 282        }
 283
 284        return count;
 285}
 286
 287static int sun4i_drv_probe(struct platform_device *pdev)
 288{
 289        struct component_match *match = NULL;
 290        struct device_node *np = pdev->dev.of_node;
 291        int i, count = 0;
 292
 293        for (i = 0;; i++) {
 294                struct device_node *pipeline = of_parse_phandle(np,
 295                                                                "allwinner,pipelines",
 296                                                                i);
 297                if (!pipeline)
 298                        break;
 299
 300                count += sun4i_drv_add_endpoints(&pdev->dev, &match,
 301                                                pipeline);
 302                of_node_put(pipeline);
 303
 304                DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
 305                                 count, i);
 306        }
 307
 308        if (count)
 309                return component_master_add_with_match(&pdev->dev,
 310                                                       &sun4i_drv_master_ops,
 311                                                       match);
 312        else
 313                return 0;
 314}
 315
 316static int sun4i_drv_remove(struct platform_device *pdev)
 317{
 318        return 0;
 319}
 320
 321static const struct of_device_id sun4i_drv_of_table[] = {
 322        { .compatible = "allwinner,sun5i-a13-display-engine" },
 323        { }
 324};
 325MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
 326
 327static struct platform_driver sun4i_drv_platform_driver = {
 328        .probe          = sun4i_drv_probe,
 329        .remove         = sun4i_drv_remove,
 330        .driver         = {
 331                .name           = "sun4i-drm",
 332                .of_match_table = sun4i_drv_of_table,
 333        },
 334};
 335module_platform_driver(sun4i_drv_platform_driver);
 336
 337MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
 338MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 339MODULE_DESCRIPTION("Allwinner A10 Display Engine DRM/KMS Driver");
 340MODULE_LICENSE("GPL");
 341