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