linux/drivers/gpu/drm/arm/hdlcd_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013-2015 ARM Limited
   3 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
   4 *
   5 * This file is subject to the terms and conditions of the GNU General Public
   6 * License.  See the file COPYING in the main directory of this archive
   7 * for more details.
   8 *
   9 *  ARM HDLCD Driver
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/spinlock.h>
  14#include <linux/clk.h>
  15#include <linux/component.h>
  16#include <linux/console.h>
  17#include <linux/dma-mapping.h>
  18#include <linux/list.h>
  19#include <linux/of_graph.h>
  20#include <linux/of_reserved_mem.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm_runtime.h>
  23
  24#include <drm/drm_atomic_helper.h>
  25#include <drm/drm_crtc.h>
  26#include <drm/drm_debugfs.h>
  27#include <drm/drm_drv.h>
  28#include <drm/drm_fb_cma_helper.h>
  29#include <drm/drm_fb_helper.h>
  30#include <drm/drm_gem_cma_helper.h>
  31#include <drm/drm_gem_framebuffer_helper.h>
  32#include <drm/drm_modeset_helper.h>
  33#include <drm/drm_of.h>
  34#include <drm/drm_probe_helper.h>
  35#include <drm/drm_vblank.h>
  36
  37#include "hdlcd_drv.h"
  38#include "hdlcd_regs.h"
  39
  40static irqreturn_t hdlcd_irq(int irq, void *arg)
  41{
  42        struct drm_device *drm = arg;
  43        struct hdlcd_drm_private *hdlcd = drm->dev_private;
  44        unsigned long irq_status;
  45
  46        irq_status = hdlcd_read(hdlcd, HDLCD_REG_INT_STATUS);
  47
  48#ifdef CONFIG_DEBUG_FS
  49        if (irq_status & HDLCD_INTERRUPT_UNDERRUN)
  50                atomic_inc(&hdlcd->buffer_underrun_count);
  51
  52        if (irq_status & HDLCD_INTERRUPT_DMA_END)
  53                atomic_inc(&hdlcd->dma_end_count);
  54
  55        if (irq_status & HDLCD_INTERRUPT_BUS_ERROR)
  56                atomic_inc(&hdlcd->bus_error_count);
  57
  58        if (irq_status & HDLCD_INTERRUPT_VSYNC)
  59                atomic_inc(&hdlcd->vsync_count);
  60
  61#endif
  62        if (irq_status & HDLCD_INTERRUPT_VSYNC)
  63                drm_crtc_handle_vblank(&hdlcd->crtc);
  64
  65        /* acknowledge interrupt(s) */
  66        hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, irq_status);
  67
  68        return IRQ_HANDLED;
  69}
  70
  71static void hdlcd_irq_preinstall(struct drm_device *drm)
  72{
  73        struct hdlcd_drm_private *hdlcd = drm->dev_private;
  74        /* Ensure interrupts are disabled */
  75        hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, 0);
  76        hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, ~0);
  77}
  78
  79static void hdlcd_irq_postinstall(struct drm_device *drm)
  80{
  81#ifdef CONFIG_DEBUG_FS
  82        struct hdlcd_drm_private *hdlcd = drm->dev_private;
  83        unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
  84
  85        /* enable debug interrupts */
  86        irq_mask |= HDLCD_DEBUG_INT_MASK;
  87
  88        hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
  89#endif
  90}
  91
  92static int hdlcd_irq_install(struct drm_device *drm, int irq)
  93{
  94        int ret;
  95
  96        if (irq == IRQ_NOTCONNECTED)
  97                return -ENOTCONN;
  98
  99        hdlcd_irq_preinstall(drm);
 100
 101        ret = request_irq(irq, hdlcd_irq, 0, drm->driver->name, drm);
 102        if (ret)
 103                return ret;
 104
 105        hdlcd_irq_postinstall(drm);
 106
 107        return 0;
 108}
 109
 110static void hdlcd_irq_uninstall(struct drm_device *drm)
 111{
 112        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 113        /* disable all the interrupts that we might have enabled */
 114        unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
 115
 116#ifdef CONFIG_DEBUG_FS
 117        /* disable debug interrupts */
 118        irq_mask &= ~HDLCD_DEBUG_INT_MASK;
 119#endif
 120
 121        /* disable vsync interrupts */
 122        irq_mask &= ~HDLCD_INTERRUPT_VSYNC;
 123        hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
 124
 125        free_irq(hdlcd->irq, drm);
 126}
 127
 128static int hdlcd_load(struct drm_device *drm, unsigned long flags)
 129{
 130        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 131        struct platform_device *pdev = to_platform_device(drm->dev);
 132        struct resource *res;
 133        u32 version;
 134        int ret;
 135
 136        hdlcd->clk = devm_clk_get(drm->dev, "pxlclk");
 137        if (IS_ERR(hdlcd->clk))
 138                return PTR_ERR(hdlcd->clk);
 139
 140#ifdef CONFIG_DEBUG_FS
 141        atomic_set(&hdlcd->buffer_underrun_count, 0);
 142        atomic_set(&hdlcd->bus_error_count, 0);
 143        atomic_set(&hdlcd->vsync_count, 0);
 144        atomic_set(&hdlcd->dma_end_count, 0);
 145#endif
 146
 147        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 148        hdlcd->mmio = devm_ioremap_resource(drm->dev, res);
 149        if (IS_ERR(hdlcd->mmio)) {
 150                DRM_ERROR("failed to map control registers area\n");
 151                ret = PTR_ERR(hdlcd->mmio);
 152                hdlcd->mmio = NULL;
 153                return ret;
 154        }
 155
 156        version = hdlcd_read(hdlcd, HDLCD_REG_VERSION);
 157        if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
 158                DRM_ERROR("unknown product id: 0x%x\n", version);
 159                return -EINVAL;
 160        }
 161        DRM_INFO("found ARM HDLCD version r%dp%d\n",
 162                (version & HDLCD_VERSION_MAJOR_MASK) >> 8,
 163                version & HDLCD_VERSION_MINOR_MASK);
 164
 165        /* Get the optional framebuffer memory resource */
 166        ret = of_reserved_mem_device_init(drm->dev);
 167        if (ret && ret != -ENODEV)
 168                return ret;
 169
 170        ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
 171        if (ret)
 172                goto setup_fail;
 173
 174        ret = hdlcd_setup_crtc(drm);
 175        if (ret < 0) {
 176                DRM_ERROR("failed to create crtc\n");
 177                goto setup_fail;
 178        }
 179
 180        ret = platform_get_irq(pdev, 0);
 181        if (ret < 0)
 182                goto irq_fail;
 183        hdlcd->irq = ret;
 184
 185        ret = hdlcd_irq_install(drm, hdlcd->irq);
 186        if (ret < 0) {
 187                DRM_ERROR("failed to install IRQ handler\n");
 188                goto irq_fail;
 189        }
 190
 191        return 0;
 192
 193irq_fail:
 194        drm_crtc_cleanup(&hdlcd->crtc);
 195setup_fail:
 196        of_reserved_mem_device_release(drm->dev);
 197
 198        return ret;
 199}
 200
 201static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = {
 202        .fb_create = drm_gem_fb_create,
 203        .atomic_check = drm_atomic_helper_check,
 204        .atomic_commit = drm_atomic_helper_commit,
 205};
 206
 207static void hdlcd_setup_mode_config(struct drm_device *drm)
 208{
 209        drm_mode_config_init(drm);
 210        drm->mode_config.min_width = 0;
 211        drm->mode_config.min_height = 0;
 212        drm->mode_config.max_width = HDLCD_MAX_XRES;
 213        drm->mode_config.max_height = HDLCD_MAX_YRES;
 214        drm->mode_config.funcs = &hdlcd_mode_config_funcs;
 215}
 216
 217#ifdef CONFIG_DEBUG_FS
 218static int hdlcd_show_underrun_count(struct seq_file *m, void *arg)
 219{
 220        struct drm_info_node *node = (struct drm_info_node *)m->private;
 221        struct drm_device *drm = node->minor->dev;
 222        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 223
 224        seq_printf(m, "underrun : %d\n", atomic_read(&hdlcd->buffer_underrun_count));
 225        seq_printf(m, "dma_end  : %d\n", atomic_read(&hdlcd->dma_end_count));
 226        seq_printf(m, "bus_error: %d\n", atomic_read(&hdlcd->bus_error_count));
 227        seq_printf(m, "vsync    : %d\n", atomic_read(&hdlcd->vsync_count));
 228        return 0;
 229}
 230
 231static int hdlcd_show_pxlclock(struct seq_file *m, void *arg)
 232{
 233        struct drm_info_node *node = (struct drm_info_node *)m->private;
 234        struct drm_device *drm = node->minor->dev;
 235        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 236        unsigned long clkrate = clk_get_rate(hdlcd->clk);
 237        unsigned long mode_clock = hdlcd->crtc.mode.crtc_clock * 1000;
 238
 239        seq_printf(m, "hw  : %lu\n", clkrate);
 240        seq_printf(m, "mode: %lu\n", mode_clock);
 241        return 0;
 242}
 243
 244static struct drm_info_list hdlcd_debugfs_list[] = {
 245        { "interrupt_count", hdlcd_show_underrun_count, 0 },
 246        { "clocks", hdlcd_show_pxlclock, 0 },
 247};
 248
 249static void hdlcd_debugfs_init(struct drm_minor *minor)
 250{
 251        drm_debugfs_create_files(hdlcd_debugfs_list,
 252                                 ARRAY_SIZE(hdlcd_debugfs_list),
 253                                 minor->debugfs_root, minor);
 254}
 255#endif
 256
 257DEFINE_DRM_GEM_CMA_FOPS(fops);
 258
 259static const struct drm_driver hdlcd_driver = {
 260        .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
 261        DRM_GEM_CMA_DRIVER_OPS,
 262#ifdef CONFIG_DEBUG_FS
 263        .debugfs_init = hdlcd_debugfs_init,
 264#endif
 265        .fops = &fops,
 266        .name = "hdlcd",
 267        .desc = "ARM HDLCD Controller DRM",
 268        .date = "20151021",
 269        .major = 1,
 270        .minor = 0,
 271};
 272
 273static int hdlcd_drm_bind(struct device *dev)
 274{
 275        struct drm_device *drm;
 276        struct hdlcd_drm_private *hdlcd;
 277        int ret;
 278
 279        hdlcd = devm_kzalloc(dev, sizeof(*hdlcd), GFP_KERNEL);
 280        if (!hdlcd)
 281                return -ENOMEM;
 282
 283        drm = drm_dev_alloc(&hdlcd_driver, dev);
 284        if (IS_ERR(drm))
 285                return PTR_ERR(drm);
 286
 287        drm->dev_private = hdlcd;
 288        dev_set_drvdata(dev, drm);
 289
 290        hdlcd_setup_mode_config(drm);
 291        ret = hdlcd_load(drm, 0);
 292        if (ret)
 293                goto err_free;
 294
 295        /* Set the CRTC's port so that the encoder component can find it */
 296        hdlcd->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
 297
 298        ret = component_bind_all(dev, drm);
 299        if (ret) {
 300                DRM_ERROR("Failed to bind all components\n");
 301                goto err_unload;
 302        }
 303
 304        ret = pm_runtime_set_active(dev);
 305        if (ret)
 306                goto err_pm_active;
 307
 308        pm_runtime_enable(dev);
 309
 310        ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
 311        if (ret < 0) {
 312                DRM_ERROR("failed to initialise vblank\n");
 313                goto err_vblank;
 314        }
 315
 316        drm_mode_config_reset(drm);
 317        drm_kms_helper_poll_init(drm);
 318
 319        ret = drm_dev_register(drm, 0);
 320        if (ret)
 321                goto err_register;
 322
 323        drm_fbdev_generic_setup(drm, 32);
 324
 325        return 0;
 326
 327err_register:
 328        drm_kms_helper_poll_fini(drm);
 329err_vblank:
 330        pm_runtime_disable(drm->dev);
 331err_pm_active:
 332        drm_atomic_helper_shutdown(drm);
 333        component_unbind_all(dev, drm);
 334err_unload:
 335        of_node_put(hdlcd->crtc.port);
 336        hdlcd->crtc.port = NULL;
 337        hdlcd_irq_uninstall(drm);
 338        of_reserved_mem_device_release(drm->dev);
 339err_free:
 340        drm_mode_config_cleanup(drm);
 341        dev_set_drvdata(dev, NULL);
 342        drm_dev_put(drm);
 343
 344        return ret;
 345}
 346
 347static void hdlcd_drm_unbind(struct device *dev)
 348{
 349        struct drm_device *drm = dev_get_drvdata(dev);
 350        struct hdlcd_drm_private *hdlcd = drm->dev_private;
 351
 352        drm_dev_unregister(drm);
 353        drm_kms_helper_poll_fini(drm);
 354        component_unbind_all(dev, drm);
 355        of_node_put(hdlcd->crtc.port);
 356        hdlcd->crtc.port = NULL;
 357        pm_runtime_get_sync(dev);
 358        drm_atomic_helper_shutdown(drm);
 359        hdlcd_irq_uninstall(drm);
 360        pm_runtime_put(dev);
 361        if (pm_runtime_enabled(dev))
 362                pm_runtime_disable(dev);
 363        of_reserved_mem_device_release(dev);
 364        drm_mode_config_cleanup(drm);
 365        drm->dev_private = NULL;
 366        dev_set_drvdata(dev, NULL);
 367        drm_dev_put(drm);
 368}
 369
 370static const struct component_master_ops hdlcd_master_ops = {
 371        .bind           = hdlcd_drm_bind,
 372        .unbind         = hdlcd_drm_unbind,
 373};
 374
 375static int compare_dev(struct device *dev, void *data)
 376{
 377        return dev->of_node == data;
 378}
 379
 380static int hdlcd_probe(struct platform_device *pdev)
 381{
 382        struct device_node *port;
 383        struct component_match *match = NULL;
 384
 385        /* there is only one output port inside each device, find it */
 386        port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
 387        if (!port)
 388                return -ENODEV;
 389
 390        drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
 391        of_node_put(port);
 392
 393        return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
 394                                               match);
 395}
 396
 397static int hdlcd_remove(struct platform_device *pdev)
 398{
 399        component_master_del(&pdev->dev, &hdlcd_master_ops);
 400        return 0;
 401}
 402
 403static const struct of_device_id  hdlcd_of_match[] = {
 404        { .compatible   = "arm,hdlcd" },
 405        {},
 406};
 407MODULE_DEVICE_TABLE(of, hdlcd_of_match);
 408
 409static int __maybe_unused hdlcd_pm_suspend(struct device *dev)
 410{
 411        struct drm_device *drm = dev_get_drvdata(dev);
 412
 413        return drm_mode_config_helper_suspend(drm);
 414}
 415
 416static int __maybe_unused hdlcd_pm_resume(struct device *dev)
 417{
 418        struct drm_device *drm = dev_get_drvdata(dev);
 419
 420        drm_mode_config_helper_resume(drm);
 421
 422        return 0;
 423}
 424
 425static SIMPLE_DEV_PM_OPS(hdlcd_pm_ops, hdlcd_pm_suspend, hdlcd_pm_resume);
 426
 427static struct platform_driver hdlcd_platform_driver = {
 428        .probe          = hdlcd_probe,
 429        .remove         = hdlcd_remove,
 430        .driver = {
 431                .name = "hdlcd",
 432                .pm = &hdlcd_pm_ops,
 433                .of_match_table = hdlcd_of_match,
 434        },
 435};
 436
 437module_platform_driver(hdlcd_platform_driver);
 438
 439MODULE_AUTHOR("Liviu Dudau");
 440MODULE_DESCRIPTION("ARM HDLCD DRM driver");
 441MODULE_LICENSE("GPL v2");
 442