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/console.h>
  15#include <linux/io.h>
  16#include <linux/mfd/syscon.h>
  17#include <linux/mm.h>
  18#include <linux/module.h>
  19#include <linux/of_platform.h>
  20#include <linux/platform_device.h>
  21#include <linux/pm.h>
  22#include <linux/pm_runtime.h>
  23#include <linux/regmap.h>
  24
  25#include <drm/drmP.h>
  26#include <drm/drm_atomic_helper.h>
  27#include <drm/drm_crtc_helper.h>
  28#include <drm/drm_fb_cma_helper.h>
  29#include <drm/drm_gem_cma_helper.h>
  30#include <drm/drm_modeset_helper.h>
  31
  32#include "fsl_dcu_drm_crtc.h"
  33#include "fsl_dcu_drm_drv.h"
  34#include "fsl_tcon.h"
  35
  36static int legacyfb_depth = 24;
  37module_param(legacyfb_depth, int, 0444);
  38
  39static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
  40{
  41        if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
  42                return true;
  43
  44        return false;
  45}
  46
  47static const struct regmap_config fsl_dcu_regmap_config = {
  48        .reg_bits = 32,
  49        .reg_stride = 4,
  50        .val_bits = 32,
  51
  52        .volatile_reg = fsl_dcu_drm_is_volatile_reg,
  53};
  54
  55static void fsl_dcu_irq_uninstall(struct drm_device *dev)
  56{
  57        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  58
  59        regmap_write(fsl_dev->regmap, DCU_INT_STATUS, ~0);
  60        regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
  61}
  62
  63static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
  64{
  65        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
  66        int ret;
  67
  68        ret = fsl_dcu_drm_modeset_init(fsl_dev);
  69        if (ret < 0) {
  70                dev_err(dev->dev, "failed to initialize mode setting\n");
  71                return ret;
  72        }
  73
  74        ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
  75        if (ret < 0) {
  76                dev_err(dev->dev, "failed to initialize vblank\n");
  77                goto done;
  78        }
  79
  80        ret = drm_irq_install(dev, fsl_dev->irq);
  81        if (ret < 0) {
  82                dev_err(dev->dev, "failed to install IRQ handler\n");
  83                goto done;
  84        }
  85
  86        if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
  87            legacyfb_depth != 32) {
  88                dev_warn(dev->dev,
  89                        "Invalid legacyfb_depth.  Defaulting to 24bpp\n");
  90                legacyfb_depth = 24;
  91        }
  92        fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1);
  93        if (IS_ERR(fsl_dev->fbdev)) {
  94                ret = PTR_ERR(fsl_dev->fbdev);
  95                fsl_dev->fbdev = NULL;
  96                goto done;
  97        }
  98
  99        return 0;
 100done:
 101        drm_kms_helper_poll_fini(dev);
 102
 103        if (fsl_dev->fbdev)
 104                drm_fbdev_cma_fini(fsl_dev->fbdev);
 105
 106        drm_mode_config_cleanup(dev);
 107        drm_irq_uninstall(dev);
 108        dev->dev_private = NULL;
 109
 110        return ret;
 111}
 112
 113static void fsl_dcu_unload(struct drm_device *dev)
 114{
 115        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 116
 117        drm_atomic_helper_shutdown(dev);
 118        drm_kms_helper_poll_fini(dev);
 119
 120        if (fsl_dev->fbdev)
 121                drm_fbdev_cma_fini(fsl_dev->fbdev);
 122
 123        drm_mode_config_cleanup(dev);
 124        drm_irq_uninstall(dev);
 125
 126        dev->dev_private = NULL;
 127}
 128
 129static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
 130{
 131        struct drm_device *dev = arg;
 132        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 133        unsigned int int_status;
 134        int ret;
 135
 136        ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
 137        if (ret) {
 138                dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
 139                return IRQ_NONE;
 140        }
 141
 142        if (int_status & DCU_INT_STATUS_VBLANK)
 143                drm_handle_vblank(dev, 0);
 144
 145        regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
 146
 147        return IRQ_HANDLED;
 148}
 149
 150static void fsl_dcu_drm_lastclose(struct drm_device *dev)
 151{
 152        struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 153
 154        drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
 155}
 156
 157DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops);
 158
 159static struct drm_driver fsl_dcu_drm_driver = {
 160        .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
 161                                | DRIVER_PRIME | DRIVER_ATOMIC,
 162        .lastclose              = fsl_dcu_drm_lastclose,
 163        .load                   = fsl_dcu_load,
 164        .unload                 = fsl_dcu_unload,
 165        .irq_handler            = fsl_dcu_drm_irq,
 166        .irq_preinstall         = fsl_dcu_irq_uninstall,
 167        .irq_uninstall          = fsl_dcu_irq_uninstall,
 168        .gem_free_object_unlocked = drm_gem_cma_free_object,
 169        .gem_vm_ops             = &drm_gem_cma_vm_ops,
 170        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
 171        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
 172        .gem_prime_import       = drm_gem_prime_import,
 173        .gem_prime_export       = drm_gem_prime_export,
 174        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
 175        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
 176        .gem_prime_vmap         = drm_gem_cma_prime_vmap,
 177        .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
 178        .gem_prime_mmap         = drm_gem_cma_prime_mmap,
 179        .dumb_create            = drm_gem_cma_dumb_create,
 180        .fops                   = &fsl_dcu_drm_fops,
 181        .name                   = "fsl-dcu-drm",
 182        .desc                   = "Freescale DCU DRM",
 183        .date                   = "20160425",
 184        .major                  = 1,
 185        .minor                  = 1,
 186};
 187
 188#ifdef CONFIG_PM_SLEEP
 189static int fsl_dcu_drm_pm_suspend(struct device *dev)
 190{
 191        struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
 192        int ret;
 193
 194        if (!fsl_dev)
 195                return 0;
 196
 197        disable_irq(fsl_dev->irq);
 198
 199        ret = drm_mode_config_helper_suspend(fsl_dev->drm);
 200        if (ret) {
 201                enable_irq(fsl_dev->irq);
 202                return ret;
 203        }
 204
 205        clk_disable_unprepare(fsl_dev->clk);
 206
 207        return 0;
 208}
 209
 210static int fsl_dcu_drm_pm_resume(struct device *dev)
 211{
 212        struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
 213        int ret;
 214
 215        if (!fsl_dev)
 216                return 0;
 217
 218        ret = clk_prepare_enable(fsl_dev->clk);
 219        if (ret < 0) {
 220                dev_err(dev, "failed to enable dcu clk\n");
 221                return ret;
 222        }
 223
 224        if (fsl_dev->tcon)
 225                fsl_tcon_bypass_enable(fsl_dev->tcon);
 226        fsl_dcu_drm_init_planes(fsl_dev->drm);
 227        enable_irq(fsl_dev->irq);
 228
 229        drm_mode_config_helper_resume(fsl_dev->drm);
 230
 231        return 0;
 232}
 233#endif
 234
 235static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
 236        SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_drm_pm_suspend, fsl_dcu_drm_pm_resume)
 237};
 238
 239static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
 240        .name = "ls1021a",
 241        .total_layer = 16,
 242        .max_layer = 4,
 243        .layer_regs = LS1021A_LAYER_REG_NUM,
 244};
 245
 246static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
 247        .name = "vf610",
 248        .total_layer = 64,
 249        .max_layer = 6,
 250        .layer_regs = VF610_LAYER_REG_NUM,
 251};
 252
 253static const struct of_device_id fsl_dcu_of_match[] = {
 254        {
 255                .compatible = "fsl,ls1021a-dcu",
 256                .data = &fsl_dcu_ls1021a_data,
 257        }, {
 258                .compatible = "fsl,vf610-dcu",
 259                .data = &fsl_dcu_vf610_data,
 260        }, {
 261        },
 262};
 263MODULE_DEVICE_TABLE(of, fsl_dcu_of_match);
 264
 265static int fsl_dcu_drm_probe(struct platform_device *pdev)
 266{
 267        struct fsl_dcu_drm_device *fsl_dev;
 268        struct drm_device *drm;
 269        struct device *dev = &pdev->dev;
 270        struct resource *res;
 271        void __iomem *base;
 272        struct drm_driver *driver = &fsl_dcu_drm_driver;
 273        struct clk *pix_clk_in;
 274        char pix_clk_name[32];
 275        const char *pix_clk_in_name;
 276        const struct of_device_id *id;
 277        int ret;
 278        u8 div_ratio_shift = 0;
 279
 280        fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
 281        if (!fsl_dev)
 282                return -ENOMEM;
 283
 284        id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
 285        if (!id)
 286                return -ENODEV;
 287        fsl_dev->soc = id->data;
 288
 289        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 290        base = devm_ioremap_resource(dev, res);
 291        if (IS_ERR(base)) {
 292                ret = PTR_ERR(base);
 293                return ret;
 294        }
 295
 296        fsl_dev->irq = platform_get_irq(pdev, 0);
 297        if (fsl_dev->irq < 0) {
 298                dev_err(dev, "failed to get irq\n");
 299                return fsl_dev->irq;
 300        }
 301
 302        fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
 303                        &fsl_dcu_regmap_config);
 304        if (IS_ERR(fsl_dev->regmap)) {
 305                dev_err(dev, "regmap init failed\n");
 306                return PTR_ERR(fsl_dev->regmap);
 307        }
 308
 309        fsl_dev->clk = devm_clk_get(dev, "dcu");
 310        if (IS_ERR(fsl_dev->clk)) {
 311                dev_err(dev, "failed to get dcu clock\n");
 312                return PTR_ERR(fsl_dev->clk);
 313        }
 314        ret = clk_prepare_enable(fsl_dev->clk);
 315        if (ret < 0) {
 316                dev_err(dev, "failed to enable dcu clk\n");
 317                return ret;
 318        }
 319
 320        pix_clk_in = devm_clk_get(dev, "pix");
 321        if (IS_ERR(pix_clk_in)) {
 322                /* legancy binding, use dcu clock as pixel clock input */
 323                pix_clk_in = fsl_dev->clk;
 324        }
 325
 326        if (of_property_read_bool(dev->of_node, "big-endian"))
 327                div_ratio_shift = 24;
 328
 329        pix_clk_in_name = __clk_get_name(pix_clk_in);
 330        snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
 331        fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
 332                        pix_clk_in_name, 0, base + DCU_DIV_RATIO,
 333                        div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
 334        if (IS_ERR(fsl_dev->pix_clk)) {
 335                dev_err(dev, "failed to register pix clk\n");
 336                ret = PTR_ERR(fsl_dev->pix_clk);
 337                goto disable_clk;
 338        }
 339
 340        fsl_dev->tcon = fsl_tcon_init(dev);
 341
 342        drm = drm_dev_alloc(driver, dev);
 343        if (IS_ERR(drm)) {
 344                ret = PTR_ERR(drm);
 345                goto unregister_pix_clk;
 346        }
 347
 348        fsl_dev->dev = dev;
 349        fsl_dev->drm = drm;
 350        fsl_dev->np = dev->of_node;
 351        drm->dev_private = fsl_dev;
 352        dev_set_drvdata(dev, fsl_dev);
 353
 354        ret = drm_dev_register(drm, 0);
 355        if (ret < 0)
 356                goto unref;
 357
 358        return 0;
 359
 360unref:
 361        drm_dev_unref(drm);
 362unregister_pix_clk:
 363        clk_unregister(fsl_dev->pix_clk);
 364disable_clk:
 365        clk_disable_unprepare(fsl_dev->clk);
 366        return ret;
 367}
 368
 369static int fsl_dcu_drm_remove(struct platform_device *pdev)
 370{
 371        struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
 372
 373        drm_dev_unregister(fsl_dev->drm);
 374        drm_dev_unref(fsl_dev->drm);
 375        clk_disable_unprepare(fsl_dev->clk);
 376        clk_unregister(fsl_dev->pix_clk);
 377
 378        return 0;
 379}
 380
 381static struct platform_driver fsl_dcu_drm_platform_driver = {
 382        .probe          = fsl_dcu_drm_probe,
 383        .remove         = fsl_dcu_drm_remove,
 384        .driver         = {
 385                .name   = "fsl-dcu",
 386                .pm     = &fsl_dcu_drm_pm_ops,
 387                .of_match_table = fsl_dcu_of_match,
 388        },
 389};
 390
 391module_platform_driver(fsl_dcu_drm_platform_driver);
 392
 393MODULE_DESCRIPTION("Freescale DCU DRM Driver");
 394MODULE_LICENSE("GPL");
 395