linux/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015 Freescale Semiconductor, Inc.
   3 *
   4 * Freescale DCU drm device driver
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/clk-provider.h>
  14#include <linux/io.h>
  15#include <linux/mfd/syscon.h>
  16#include <linux/mm.h>
  17#include <linux/module.h>
  18#include <linux/of_platform.h>
  19#include <linux/platform_device.h>
  20#include <linux/pm.h>
  21#include <linux/pm_runtime.h>
  22#include <linux/regmap.h>
  23
  24#include <drm/drmP.h>
  25#include <drm/drm_crtc_helper.h>
  26#include <drm/drm_gem_cma_helper.h>
  27
  28#include "fsl_dcu_drm_crtc.h"
  29#include "fsl_dcu_drm_drv.h"
  30
  31static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
  32{
  33        if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
  34                return true;
  35
  36        return false;
  37}
  38
  39static const struct regmap_config fsl_dcu_regmap_config = {
  40        .reg_bits = 32,
  41        .reg_stride = 4,
  42        .val_bits = 32,
  43        .cache_type = REGCACHE_RBTREE,
  44
  45        .volatile_reg = fsl_dcu_drm_is_volatile_reg,
  46};
  47
  48static int fsl_dcu_drm_irq_init(struct drm_device *dev)
  49{
  50        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  51        int ret;
  52
  53        ret = drm_irq_install(dev, fsl_dev->irq);
  54        if (ret < 0)
  55                dev_err(dev->dev, "failed to install IRQ handler\n");
  56
  57        regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0);
  58        regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
  59        regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
  60                     DCU_UPDATE_MODE_READREG);
  61
  62        return ret;
  63}
  64
  65static int fsl_dcu_load(struct drm_device *drm, unsigned long flags)
  66{
  67        struct device *dev = drm->dev;
  68        struct fsl_dcu_drm_device *fsl_dev = drm->dev_private;
  69        int ret;
  70
  71        ret = fsl_dcu_drm_modeset_init(fsl_dev);
  72        if (ret < 0) {
  73                dev_err(dev, "failed to initialize mode setting\n");
  74                return ret;
  75        }
  76
  77        ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
  78        if (ret < 0) {
  79                dev_err(dev, "failed to initialize vblank\n");
  80                goto done;
  81        }
  82        drm->vblank_disable_allowed = true;
  83
  84        ret = fsl_dcu_drm_irq_init(drm);
  85        if (ret < 0)
  86                goto done;
  87        drm->irq_enabled = true;
  88
  89        fsl_dcu_fbdev_init(drm);
  90
  91        return 0;
  92done:
  93        if (ret) {
  94                drm_mode_config_cleanup(drm);
  95                drm_vblank_cleanup(drm);
  96                drm_irq_uninstall(drm);
  97                drm->dev_private = NULL;
  98        }
  99
 100        return ret;
 101}
 102
 103static int fsl_dcu_unload(struct drm_device *dev)
 104{
 105        drm_mode_config_cleanup(dev);
 106        drm_vblank_cleanup(dev);
 107        drm_irq_uninstall(dev);
 108
 109        dev->dev_private = NULL;
 110
 111        return 0;
 112}
 113
 114static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
 115{
 116        struct drm_device *dev = arg;
 117        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 118        unsigned int int_status;
 119        int ret;
 120
 121        ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
 122        if (ret) {
 123                dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
 124                return IRQ_NONE;
 125        }
 126
 127        if (int_status & DCU_INT_STATUS_VBLANK)
 128                drm_handle_vblank(dev, 0);
 129
 130        regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
 131        regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
 132                     DCU_UPDATE_MODE_READREG);
 133
 134        return IRQ_HANDLED;
 135}
 136
 137static int fsl_dcu_drm_enable_vblank(struct drm_device *dev, unsigned int pipe)
 138{
 139        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 140        unsigned int value;
 141
 142        regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
 143        value &= ~DCU_INT_MASK_VBLANK;
 144        regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
 145
 146        return 0;
 147}
 148
 149static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
 150                                       unsigned int pipe)
 151{
 152        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 153        unsigned int value;
 154
 155        regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
 156        value |= DCU_INT_MASK_VBLANK;
 157        regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
 158}
 159
 160static const struct file_operations fsl_dcu_drm_fops = {
 161        .owner          = THIS_MODULE,
 162        .open           = drm_open,
 163        .release        = drm_release,
 164        .unlocked_ioctl = drm_ioctl,
 165#ifdef CONFIG_COMPAT
 166        .compat_ioctl   = drm_compat_ioctl,
 167#endif
 168        .poll           = drm_poll,
 169        .read           = drm_read,
 170        .llseek         = no_llseek,
 171        .mmap           = drm_gem_cma_mmap,
 172};
 173
 174static struct drm_driver fsl_dcu_drm_driver = {
 175        .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
 176                                | DRIVER_PRIME | DRIVER_ATOMIC,
 177        .load                   = fsl_dcu_load,
 178        .unload                 = fsl_dcu_unload,
 179        .irq_handler            = fsl_dcu_drm_irq,
 180        .get_vblank_counter     = drm_vblank_no_hw_counter,
 181        .enable_vblank          = fsl_dcu_drm_enable_vblank,
 182        .disable_vblank         = fsl_dcu_drm_disable_vblank,
 183        .gem_free_object        = drm_gem_cma_free_object,
 184        .gem_vm_ops             = &drm_gem_cma_vm_ops,
 185        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
 186        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
 187        .gem_prime_import       = drm_gem_prime_import,
 188        .gem_prime_export       = drm_gem_prime_export,
 189        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
 190        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
 191        .gem_prime_vmap         = drm_gem_cma_prime_vmap,
 192        .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
 193        .gem_prime_mmap         = drm_gem_cma_prime_mmap,
 194        .dumb_create            = drm_gem_cma_dumb_create,
 195        .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
 196        .dumb_destroy           = drm_gem_dumb_destroy,
 197        .fops                   = &fsl_dcu_drm_fops,
 198        .name                   = "fsl-dcu-drm",
 199        .desc                   = "Freescale DCU DRM",
 200        .date                   = "20150213",
 201        .major                  = 1,
 202        .minor                  = 0,
 203};
 204
 205#ifdef CONFIG_PM_SLEEP
 206static int fsl_dcu_drm_pm_suspend(struct device *dev)
 207{
 208        struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
 209
 210        if (!fsl_dev)
 211                return 0;
 212
 213        drm_kms_helper_poll_disable(fsl_dev->drm);
 214        regcache_cache_only(fsl_dev->regmap, true);
 215        regcache_mark_dirty(fsl_dev->regmap);
 216        clk_disable(fsl_dev->clk);
 217        clk_unprepare(fsl_dev->clk);
 218
 219        return 0;
 220}
 221
 222static int fsl_dcu_drm_pm_resume(struct device *dev)
 223{
 224        struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
 225        int ret;
 226
 227        if (!fsl_dev)
 228                return 0;
 229
 230        ret = clk_enable(fsl_dev->clk);
 231        if (ret < 0) {
 232                dev_err(dev, "failed to enable dcu clk\n");
 233                clk_unprepare(fsl_dev->clk);
 234                return ret;
 235        }
 236        ret = clk_prepare(fsl_dev->clk);
 237        if (ret < 0) {
 238                dev_err(dev, "failed to prepare dcu clk\n");
 239                return ret;
 240        }
 241
 242        drm_kms_helper_poll_enable(fsl_dev->drm);
 243        regcache_cache_only(fsl_dev->regmap, false);
 244        regcache_sync(fsl_dev->regmap);
 245
 246        return 0;
 247}
 248#endif
 249
 250static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
 251        SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_drm_pm_suspend, fsl_dcu_drm_pm_resume)
 252};
 253
 254static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
 255        .name = "ls1021a",
 256        .total_layer = 16,
 257        .max_layer = 4,
 258};
 259
 260static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
 261        .name = "vf610",
 262        .total_layer = 64,
 263        .max_layer = 6,
 264};
 265
 266static const struct of_device_id fsl_dcu_of_match[] = {
 267        {
 268                .compatible = "fsl,ls1021a-dcu",
 269                .data = &fsl_dcu_ls1021a_data,
 270        }, {
 271                .compatible = "fsl,vf610-dcu",
 272                .data = &fsl_dcu_vf610_data,
 273        }, {
 274        },
 275};
 276MODULE_DEVICE_TABLE(of, fsl_dcu_of_match);
 277
 278static int fsl_dcu_drm_probe(struct platform_device *pdev)
 279{
 280        struct fsl_dcu_drm_device *fsl_dev;
 281        struct drm_device *drm;
 282        struct device *dev = &pdev->dev;
 283        struct resource *res;
 284        void __iomem *base;
 285        struct drm_driver *driver = &fsl_dcu_drm_driver;
 286        const struct of_device_id *id;
 287        int ret;
 288
 289        fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
 290        if (!fsl_dev)
 291                return -ENOMEM;
 292
 293        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 294        if (!res) {
 295                dev_err(dev, "could not get memory IO resource\n");
 296                return -ENODEV;
 297        }
 298
 299        base = devm_ioremap_resource(dev, res);
 300        if (IS_ERR(base)) {
 301                ret = PTR_ERR(base);
 302                return ret;
 303        }
 304
 305        fsl_dev->irq = platform_get_irq(pdev, 0);
 306        if (fsl_dev->irq < 0) {
 307                dev_err(dev, "failed to get irq\n");
 308                return -ENXIO;
 309        }
 310
 311        fsl_dev->clk = devm_clk_get(dev, "dcu");
 312        if (IS_ERR(fsl_dev->clk)) {
 313                ret = PTR_ERR(fsl_dev->clk);
 314                dev_err(dev, "failed to get dcu clock\n");
 315                return ret;
 316        }
 317        ret = clk_prepare(fsl_dev->clk);
 318        if (ret < 0) {
 319                dev_err(dev, "failed to prepare dcu clk\n");
 320                return ret;
 321        }
 322        ret = clk_enable(fsl_dev->clk);
 323        if (ret < 0) {
 324                dev_err(dev, "failed to enable dcu clk\n");
 325                clk_unprepare(fsl_dev->clk);
 326                return ret;
 327        }
 328
 329        fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
 330                        &fsl_dcu_regmap_config);
 331        if (IS_ERR(fsl_dev->regmap)) {
 332                dev_err(dev, "regmap init failed\n");
 333                return PTR_ERR(fsl_dev->regmap);
 334        }
 335
 336        id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
 337        if (!id)
 338                return -ENODEV;
 339        fsl_dev->soc = id->data;
 340
 341        drm = drm_dev_alloc(driver, dev);
 342        if (!drm)
 343                return -ENOMEM;
 344
 345        fsl_dev->dev = dev;
 346        fsl_dev->drm = drm;
 347        fsl_dev->np = dev->of_node;
 348        drm->dev_private = fsl_dev;
 349        dev_set_drvdata(dev, fsl_dev);
 350
 351        ret = drm_dev_register(drm, 0);
 352        if (ret < 0)
 353                goto unref;
 354
 355        DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
 356                 driver->major, driver->minor, driver->patchlevel,
 357                 driver->date, drm->primary->index);
 358
 359        return 0;
 360
 361unref:
 362        drm_dev_unref(drm);
 363        return ret;
 364}
 365
 366static int fsl_dcu_drm_remove(struct platform_device *pdev)
 367{
 368        struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
 369
 370        drm_put_dev(fsl_dev->drm);
 371
 372        return 0;
 373}
 374
 375static struct platform_driver fsl_dcu_drm_platform_driver = {
 376        .probe          = fsl_dcu_drm_probe,
 377        .remove         = fsl_dcu_drm_remove,
 378        .driver         = {
 379                .name   = "fsl-dcu",
 380                .pm     = &fsl_dcu_drm_pm_ops,
 381                .of_match_table = fsl_dcu_of_match,
 382        },
 383};
 384
 385module_platform_driver(fsl_dcu_drm_platform_driver);
 386
 387MODULE_DESCRIPTION("Freescale DCU DRM Driver");
 388MODULE_LICENSE("GPL");
 389