linux/drivers/gpu/drm/rcar-du/rcar_du_drv.c
<<
>>
Prefs
   1/*
   2 * rcar_du_drv.c  --  R-Car Display Unit DRM driver
   3 *
   4 * Copyright (C) 2013 Renesas Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/io.h>
  16#include <linux/mm.h>
  17#include <linux/module.h>
  18#include <linux/platform_device.h>
  19#include <linux/pm.h>
  20#include <linux/slab.h>
  21
  22#include <drm/drmP.h>
  23#include <drm/drm_crtc_helper.h>
  24#include <drm/drm_gem_cma_helper.h>
  25
  26#include "rcar_du_crtc.h"
  27#include "rcar_du_drv.h"
  28#include "rcar_du_kms.h"
  29#include "rcar_du_regs.h"
  30
  31/* -----------------------------------------------------------------------------
  32 * Core device operations
  33 */
  34
  35/*
  36 * rcar_du_get - Acquire a reference to the DU
  37 *
  38 * Acquiring a reference enables the device clock and setup core registers. A
  39 * reference must be held before accessing any hardware registers.
  40 *
  41 * This function must be called with the DRM mode_config lock held.
  42 *
  43 * Return 0 in case of success or a negative error code otherwise.
  44 */
  45int rcar_du_get(struct rcar_du_device *rcdu)
  46{
  47        int ret;
  48
  49        if (rcdu->use_count)
  50                goto done;
  51
  52        /* Enable clocks before accessing the hardware. */
  53        ret = clk_prepare_enable(rcdu->clock);
  54        if (ret < 0)
  55                return ret;
  56
  57        /* Enable extended features */
  58        rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
  59        rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
  60        rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
  61        rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
  62        rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
  63
  64        /* Use DS1PR and DS2PR to configure planes priorities and connects the
  65         * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
  66         */
  67        rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
  68
  69done:
  70        rcdu->use_count++;
  71        return 0;
  72}
  73
  74/*
  75 * rcar_du_put - Release a reference to the DU
  76 *
  77 * Releasing the last reference disables the device clock.
  78 *
  79 * This function must be called with the DRM mode_config lock held.
  80 */
  81void rcar_du_put(struct rcar_du_device *rcdu)
  82{
  83        if (--rcdu->use_count)
  84                return;
  85
  86        clk_disable_unprepare(rcdu->clock);
  87}
  88
  89/* -----------------------------------------------------------------------------
  90 * DRM operations
  91 */
  92
  93static int rcar_du_unload(struct drm_device *dev)
  94{
  95        drm_kms_helper_poll_fini(dev);
  96        drm_mode_config_cleanup(dev);
  97        drm_vblank_cleanup(dev);
  98        drm_irq_uninstall(dev);
  99
 100        dev->dev_private = NULL;
 101
 102        return 0;
 103}
 104
 105static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 106{
 107        struct platform_device *pdev = dev->platformdev;
 108        struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
 109        struct rcar_du_device *rcdu;
 110        struct resource *ioarea;
 111        struct resource *mem;
 112        int ret;
 113
 114        if (pdata == NULL) {
 115                dev_err(dev->dev, "no platform data\n");
 116                return -ENODEV;
 117        }
 118
 119        rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
 120        if (rcdu == NULL) {
 121                dev_err(dev->dev, "failed to allocate private data\n");
 122                return -ENOMEM;
 123        }
 124
 125        rcdu->dev = &pdev->dev;
 126        rcdu->pdata = pdata;
 127        rcdu->ddev = dev;
 128        dev->dev_private = rcdu;
 129
 130        /* I/O resources and clocks */
 131        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 132        if (mem == NULL) {
 133                dev_err(&pdev->dev, "failed to get memory resource\n");
 134                return -EINVAL;
 135        }
 136
 137        ioarea = devm_request_mem_region(&pdev->dev, mem->start,
 138                                         resource_size(mem), pdev->name);
 139        if (ioarea == NULL) {
 140                dev_err(&pdev->dev, "failed to request memory region\n");
 141                return -EBUSY;
 142        }
 143
 144        rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
 145                                          resource_size(ioarea));
 146        if (rcdu->mmio == NULL) {
 147                dev_err(&pdev->dev, "failed to remap memory resource\n");
 148                return -ENOMEM;
 149        }
 150
 151        rcdu->clock = devm_clk_get(&pdev->dev, NULL);
 152        if (IS_ERR(rcdu->clock)) {
 153                dev_err(&pdev->dev, "failed to get clock\n");
 154                return -ENOENT;
 155        }
 156
 157        /* DRM/KMS objects */
 158        ret = rcar_du_modeset_init(rcdu);
 159        if (ret < 0) {
 160                dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
 161                goto done;
 162        }
 163
 164        /* IRQ and vblank handling */
 165        ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
 166        if (ret < 0) {
 167                dev_err(&pdev->dev, "failed to initialize vblank\n");
 168                goto done;
 169        }
 170
 171        ret = drm_irq_install(dev);
 172        if (ret < 0) {
 173                dev_err(&pdev->dev, "failed to install IRQ handler\n");
 174                goto done;
 175        }
 176
 177        platform_set_drvdata(pdev, rcdu);
 178
 179done:
 180        if (ret)
 181                rcar_du_unload(dev);
 182
 183        return ret;
 184}
 185
 186static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
 187{
 188        struct rcar_du_device *rcdu = dev->dev_private;
 189        unsigned int i;
 190
 191        for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
 192                rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
 193}
 194
 195static irqreturn_t rcar_du_irq(int irq, void *arg)
 196{
 197        struct drm_device *dev = arg;
 198        struct rcar_du_device *rcdu = dev->dev_private;
 199        unsigned int i;
 200
 201        for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
 202                rcar_du_crtc_irq(&rcdu->crtcs[i]);
 203
 204        return IRQ_HANDLED;
 205}
 206
 207static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
 208{
 209        struct rcar_du_device *rcdu = dev->dev_private;
 210
 211        rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
 212
 213        return 0;
 214}
 215
 216static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
 217{
 218        struct rcar_du_device *rcdu = dev->dev_private;
 219
 220        rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
 221}
 222
 223static const struct file_operations rcar_du_fops = {
 224        .owner          = THIS_MODULE,
 225        .open           = drm_open,
 226        .release        = drm_release,
 227        .unlocked_ioctl = drm_ioctl,
 228#ifdef CONFIG_COMPAT
 229        .compat_ioctl   = drm_compat_ioctl,
 230#endif
 231        .poll           = drm_poll,
 232        .read           = drm_read,
 233        .fasync         = drm_fasync,
 234        .llseek         = no_llseek,
 235        .mmap           = drm_gem_cma_mmap,
 236};
 237
 238static struct drm_driver rcar_du_driver = {
 239        .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
 240                                | DRIVER_PRIME,
 241        .load                   = rcar_du_load,
 242        .unload                 = rcar_du_unload,
 243        .preclose               = rcar_du_preclose,
 244        .irq_handler            = rcar_du_irq,
 245        .get_vblank_counter     = drm_vblank_count,
 246        .enable_vblank          = rcar_du_enable_vblank,
 247        .disable_vblank         = rcar_du_disable_vblank,
 248        .gem_free_object        = drm_gem_cma_free_object,
 249        .gem_vm_ops             = &drm_gem_cma_vm_ops,
 250        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
 251        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
 252        .gem_prime_import       = drm_gem_prime_import,
 253        .gem_prime_export       = drm_gem_prime_export,
 254        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
 255        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
 256        .gem_prime_vmap         = drm_gem_cma_prime_vmap,
 257        .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
 258        .gem_prime_mmap         = drm_gem_cma_prime_mmap,
 259        .dumb_create            = rcar_du_dumb_create,
 260        .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
 261        .dumb_destroy           = drm_gem_cma_dumb_destroy,
 262        .fops                   = &rcar_du_fops,
 263        .name                   = "rcar-du",
 264        .desc                   = "Renesas R-Car Display Unit",
 265        .date                   = "20130110",
 266        .major                  = 1,
 267        .minor                  = 0,
 268};
 269
 270/* -----------------------------------------------------------------------------
 271 * Power management
 272 */
 273
 274#if CONFIG_PM_SLEEP
 275static int rcar_du_pm_suspend(struct device *dev)
 276{
 277        struct rcar_du_device *rcdu = dev_get_drvdata(dev);
 278
 279        drm_kms_helper_poll_disable(rcdu->ddev);
 280        /* TODO Suspend the CRTC */
 281
 282        return 0;
 283}
 284
 285static int rcar_du_pm_resume(struct device *dev)
 286{
 287        struct rcar_du_device *rcdu = dev_get_drvdata(dev);
 288
 289        /* TODO Resume the CRTC */
 290
 291        drm_kms_helper_poll_enable(rcdu->ddev);
 292        return 0;
 293}
 294#endif
 295
 296static const struct dev_pm_ops rcar_du_pm_ops = {
 297        SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
 298};
 299
 300/* -----------------------------------------------------------------------------
 301 * Platform driver
 302 */
 303
 304static int rcar_du_probe(struct platform_device *pdev)
 305{
 306        return drm_platform_init(&rcar_du_driver, pdev);
 307}
 308
 309static int rcar_du_remove(struct platform_device *pdev)
 310{
 311        drm_platform_exit(&rcar_du_driver, pdev);
 312
 313        return 0;
 314}
 315
 316static struct platform_driver rcar_du_platform_driver = {
 317        .probe          = rcar_du_probe,
 318        .remove         = rcar_du_remove,
 319        .driver         = {
 320                .owner  = THIS_MODULE,
 321                .name   = "rcar-du",
 322                .pm     = &rcar_du_pm_ops,
 323        },
 324};
 325
 326module_platform_driver(rcar_du_platform_driver);
 327
 328MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 329MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
 330MODULE_LICENSE("GPL");
 331