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