linux/drivers/gpu/drm/tilcdc/tilcdc_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Texas Instruments
   3 * Author: Rob Clark <robdclark@gmail.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published by
   7 * the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along with
  15 * this program.  If not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18/* LCDC DRM driver, based on da8xx-fb */
  19
  20#include <linux/component.h>
  21#include <linux/pinctrl/consumer.h>
  22#include <linux/suspend.h>
  23#include <drm/drm_atomic.h>
  24#include <drm/drm_atomic_helper.h>
  25#include <drm/drm_fb_helper.h>
  26
  27#include "tilcdc_drv.h"
  28#include "tilcdc_regs.h"
  29#include "tilcdc_tfp410.h"
  30#include "tilcdc_panel.h"
  31#include "tilcdc_external.h"
  32
  33static LIST_HEAD(module_list);
  34
  35static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 };
  36
  37static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565,
  38                                               DRM_FORMAT_BGR888,
  39                                               DRM_FORMAT_XBGR8888 };
  40
  41static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565,
  42                                              DRM_FORMAT_RGB888,
  43                                              DRM_FORMAT_XRGB8888 };
  44
  45static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565,
  46                                             DRM_FORMAT_RGB888,
  47                                             DRM_FORMAT_XRGB8888 };
  48
  49void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
  50                const struct tilcdc_module_ops *funcs)
  51{
  52        mod->name = name;
  53        mod->funcs = funcs;
  54        INIT_LIST_HEAD(&mod->list);
  55        list_add(&mod->list, &module_list);
  56}
  57
  58void tilcdc_module_cleanup(struct tilcdc_module *mod)
  59{
  60        list_del(&mod->list);
  61}
  62
  63static struct of_device_id tilcdc_of_match[];
  64
  65static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
  66                struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
  67{
  68        return drm_fb_cma_create(dev, file_priv, mode_cmd);
  69}
  70
  71static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
  72{
  73        struct tilcdc_drm_private *priv = dev->dev_private;
  74        drm_fbdev_cma_hotplug_event(priv->fbdev);
  75}
  76
  77static int tilcdc_atomic_check(struct drm_device *dev,
  78                               struct drm_atomic_state *state)
  79{
  80        int ret;
  81
  82        ret = drm_atomic_helper_check_modeset(dev, state);
  83        if (ret)
  84                return ret;
  85
  86        ret = drm_atomic_helper_check_planes(dev, state);
  87        if (ret)
  88                return ret;
  89
  90        /*
  91         * tilcdc ->atomic_check can update ->mode_changed if pixel format
  92         * changes, hence will we check modeset changes again.
  93         */
  94        ret = drm_atomic_helper_check_modeset(dev, state);
  95        if (ret)
  96                return ret;
  97
  98        return ret;
  99}
 100
 101static int tilcdc_commit(struct drm_device *dev,
 102                  struct drm_atomic_state *state,
 103                  bool async)
 104{
 105        int ret;
 106
 107        ret = drm_atomic_helper_prepare_planes(dev, state);
 108        if (ret)
 109                return ret;
 110
 111        ret = drm_atomic_helper_swap_state(state, true);
 112        if (ret) {
 113                drm_atomic_helper_cleanup_planes(dev, state);
 114                return ret;
 115        }
 116
 117        /*
 118         * Everything below can be run asynchronously without the need to grab
 119         * any modeset locks at all under one condition: It must be guaranteed
 120         * that the asynchronous work has either been cancelled (if the driver
 121         * supports it, which at least requires that the framebuffers get
 122         * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
 123         * before the new state gets committed on the software side with
 124         * drm_atomic_helper_swap_state().
 125         *
 126         * This scheme allows new atomic state updates to be prepared and
 127         * checked in parallel to the asynchronous completion of the previous
 128         * update. Which is important since compositors need to figure out the
 129         * composition of the next frame right after having submitted the
 130         * current layout.
 131         */
 132
 133        drm_atomic_helper_commit_modeset_disables(dev, state);
 134
 135        drm_atomic_helper_commit_planes(dev, state, 0);
 136
 137        drm_atomic_helper_commit_modeset_enables(dev, state);
 138
 139        drm_atomic_helper_wait_for_vblanks(dev, state);
 140
 141        drm_atomic_helper_cleanup_planes(dev, state);
 142
 143        return 0;
 144}
 145
 146static const struct drm_mode_config_funcs mode_config_funcs = {
 147        .fb_create = tilcdc_fb_create,
 148        .output_poll_changed = tilcdc_fb_output_poll_changed,
 149        .atomic_check = tilcdc_atomic_check,
 150        .atomic_commit = tilcdc_commit,
 151};
 152
 153static void modeset_init(struct drm_device *dev)
 154{
 155        struct tilcdc_drm_private *priv = dev->dev_private;
 156        struct tilcdc_module *mod;
 157
 158        list_for_each_entry(mod, &module_list, list) {
 159                DBG("loading module: %s", mod->name);
 160                mod->funcs->modeset_init(mod, dev);
 161        }
 162
 163        dev->mode_config.min_width = 0;
 164        dev->mode_config.min_height = 0;
 165        dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc);
 166        dev->mode_config.max_height = 2048;
 167        dev->mode_config.funcs = &mode_config_funcs;
 168}
 169
 170#ifdef CONFIG_CPU_FREQ
 171static int cpufreq_transition(struct notifier_block *nb,
 172                                     unsigned long val, void *data)
 173{
 174        struct tilcdc_drm_private *priv = container_of(nb,
 175                        struct tilcdc_drm_private, freq_transition);
 176
 177        if (val == CPUFREQ_POSTCHANGE)
 178                tilcdc_crtc_update_clk(priv->crtc);
 179
 180        return 0;
 181}
 182#endif
 183
 184/*
 185 * DRM operations:
 186 */
 187
 188static void tilcdc_fini(struct drm_device *dev)
 189{
 190        struct tilcdc_drm_private *priv = dev->dev_private;
 191
 192        if (priv->crtc)
 193                tilcdc_crtc_shutdown(priv->crtc);
 194
 195        if (priv->is_registered)
 196                drm_dev_unregister(dev);
 197
 198        drm_kms_helper_poll_fini(dev);
 199
 200        if (priv->fbdev)
 201                drm_fbdev_cma_fini(priv->fbdev);
 202
 203        drm_irq_uninstall(dev);
 204        drm_mode_config_cleanup(dev);
 205        tilcdc_remove_external_device(dev);
 206
 207#ifdef CONFIG_CPU_FREQ
 208        if (priv->freq_transition.notifier_call)
 209                cpufreq_unregister_notifier(&priv->freq_transition,
 210                                            CPUFREQ_TRANSITION_NOTIFIER);
 211#endif
 212
 213        if (priv->clk)
 214                clk_put(priv->clk);
 215
 216        if (priv->mmio)
 217                iounmap(priv->mmio);
 218
 219        if (priv->wq) {
 220                flush_workqueue(priv->wq);
 221                destroy_workqueue(priv->wq);
 222        }
 223
 224        dev->dev_private = NULL;
 225
 226        pm_runtime_disable(dev->dev);
 227
 228        drm_dev_unref(dev);
 229}
 230
 231static int tilcdc_init(struct drm_driver *ddrv, struct device *dev)
 232{
 233        struct drm_device *ddev;
 234        struct platform_device *pdev = to_platform_device(dev);
 235        struct device_node *node = dev->of_node;
 236        struct tilcdc_drm_private *priv;
 237        struct resource *res;
 238        u32 bpp = 0;
 239        int ret;
 240
 241        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 242        if (!priv) {
 243                dev_err(dev, "failed to allocate private data\n");
 244                return -ENOMEM;
 245        }
 246
 247        ddev = drm_dev_alloc(ddrv, dev);
 248        if (IS_ERR(ddev))
 249                return PTR_ERR(ddev);
 250
 251        ddev->dev_private = priv;
 252        platform_set_drvdata(pdev, ddev);
 253        drm_mode_config_init(ddev);
 254
 255        priv->is_componentized =
 256                tilcdc_get_external_components(dev, NULL) > 0;
 257
 258        priv->wq = alloc_ordered_workqueue("tilcdc", 0);
 259        if (!priv->wq) {
 260                ret = -ENOMEM;
 261                goto init_failed;
 262        }
 263
 264        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 265        if (!res) {
 266                dev_err(dev, "failed to get memory resource\n");
 267                ret = -EINVAL;
 268                goto init_failed;
 269        }
 270
 271        priv->mmio = ioremap_nocache(res->start, resource_size(res));
 272        if (!priv->mmio) {
 273                dev_err(dev, "failed to ioremap\n");
 274                ret = -ENOMEM;
 275                goto init_failed;
 276        }
 277
 278        priv->clk = clk_get(dev, "fck");
 279        if (IS_ERR(priv->clk)) {
 280                dev_err(dev, "failed to get functional clock\n");
 281                ret = -ENODEV;
 282                goto init_failed;
 283        }
 284
 285#ifdef CONFIG_CPU_FREQ
 286        priv->freq_transition.notifier_call = cpufreq_transition;
 287        ret = cpufreq_register_notifier(&priv->freq_transition,
 288                        CPUFREQ_TRANSITION_NOTIFIER);
 289        if (ret) {
 290                dev_err(dev, "failed to register cpufreq notifier\n");
 291                priv->freq_transition.notifier_call = NULL;
 292                goto init_failed;
 293        }
 294#endif
 295
 296        if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth))
 297                priv->max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH;
 298
 299        DBG("Maximum Bandwidth Value %d", priv->max_bandwidth);
 300
 301        if (of_property_read_u32(node, "max-width", &priv->max_width))
 302                priv->max_width = TILCDC_DEFAULT_MAX_WIDTH;
 303
 304        DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width);
 305
 306        if (of_property_read_u32(node, "max-pixelclock",
 307                                        &priv->max_pixelclock))
 308                priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;
 309
 310        DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);
 311
 312        pm_runtime_enable(dev);
 313
 314        /* Determine LCD IP Version */
 315        pm_runtime_get_sync(dev);
 316        switch (tilcdc_read(ddev, LCDC_PID_REG)) {
 317        case 0x4c100102:
 318                priv->rev = 1;
 319                break;
 320        case 0x4f200800:
 321        case 0x4f201000:
 322                priv->rev = 2;
 323                break;
 324        default:
 325                dev_warn(dev, "Unknown PID Reg value 0x%08x, "
 326                        "defaulting to LCD revision 1\n",
 327                        tilcdc_read(ddev, LCDC_PID_REG));
 328                priv->rev = 1;
 329                break;
 330        }
 331
 332        pm_runtime_put_sync(dev);
 333
 334        if (priv->rev == 1) {
 335                DBG("Revision 1 LCDC supports only RGB565 format");
 336                priv->pixelformats = tilcdc_rev1_formats;
 337                priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats);
 338                bpp = 16;
 339        } else {
 340                const char *str = "\0";
 341
 342                of_property_read_string(node, "blue-and-red-wiring", &str);
 343                if (0 == strcmp(str, "crossed")) {
 344                        DBG("Configured for crossed blue and red wires");
 345                        priv->pixelformats = tilcdc_crossed_formats;
 346                        priv->num_pixelformats =
 347                                ARRAY_SIZE(tilcdc_crossed_formats);
 348                        bpp = 32; /* Choose bpp with RGB support for fbdef */
 349                } else if (0 == strcmp(str, "straight")) {
 350                        DBG("Configured for straight blue and red wires");
 351                        priv->pixelformats = tilcdc_straight_formats;
 352                        priv->num_pixelformats =
 353                                ARRAY_SIZE(tilcdc_straight_formats);
 354                        bpp = 16; /* Choose bpp with RGB support for fbdef */
 355                } else {
 356                        DBG("Blue and red wiring '%s' unknown, use legacy mode",
 357                            str);
 358                        priv->pixelformats = tilcdc_legacy_formats;
 359                        priv->num_pixelformats =
 360                                ARRAY_SIZE(tilcdc_legacy_formats);
 361                        bpp = 16; /* This is just a guess */
 362                }
 363        }
 364
 365        ret = tilcdc_crtc_create(ddev);
 366        if (ret < 0) {
 367                dev_err(dev, "failed to create crtc\n");
 368                goto init_failed;
 369        }
 370        modeset_init(ddev);
 371
 372        if (priv->is_componentized) {
 373                ret = component_bind_all(dev, ddev);
 374                if (ret < 0)
 375                        goto init_failed;
 376
 377                ret = tilcdc_add_component_encoder(ddev);
 378                if (ret < 0)
 379                        goto init_failed;
 380        } else {
 381                ret = tilcdc_attach_external_device(ddev);
 382                if (ret)
 383                        goto init_failed;
 384        }
 385
 386        if (!priv->external_connector &&
 387            ((priv->num_encoders == 0) || (priv->num_connectors == 0))) {
 388                dev_err(dev, "no encoders/connectors found\n");
 389                ret = -ENXIO;
 390                goto init_failed;
 391        }
 392
 393        ret = drm_vblank_init(ddev, 1);
 394        if (ret < 0) {
 395                dev_err(dev, "failed to initialize vblank\n");
 396                goto init_failed;
 397        }
 398
 399        ret = drm_irq_install(ddev, platform_get_irq(pdev, 0));
 400        if (ret < 0) {
 401                dev_err(dev, "failed to install IRQ handler\n");
 402                goto init_failed;
 403        }
 404
 405        drm_mode_config_reset(ddev);
 406
 407        priv->fbdev = drm_fbdev_cma_init(ddev, bpp,
 408                                         ddev->mode_config.num_connector);
 409        if (IS_ERR(priv->fbdev)) {
 410                ret = PTR_ERR(priv->fbdev);
 411                goto init_failed;
 412        }
 413
 414        drm_kms_helper_poll_init(ddev);
 415
 416        ret = drm_dev_register(ddev, 0);
 417        if (ret)
 418                goto init_failed;
 419
 420        priv->is_registered = true;
 421        return 0;
 422
 423init_failed:
 424        tilcdc_fini(ddev);
 425
 426        return ret;
 427}
 428
 429static void tilcdc_lastclose(struct drm_device *dev)
 430{
 431        struct tilcdc_drm_private *priv = dev->dev_private;
 432        drm_fbdev_cma_restore_mode(priv->fbdev);
 433}
 434
 435static irqreturn_t tilcdc_irq(int irq, void *arg)
 436{
 437        struct drm_device *dev = arg;
 438        struct tilcdc_drm_private *priv = dev->dev_private;
 439        return tilcdc_crtc_irq(priv->crtc);
 440}
 441
 442#if defined(CONFIG_DEBUG_FS)
 443static const struct {
 444        const char *name;
 445        uint8_t  rev;
 446        uint8_t  save;
 447        uint32_t reg;
 448} registers[] =         {
 449#define REG(rev, save, reg) { #reg, rev, save, reg }
 450                /* exists in revision 1: */
 451                REG(1, false, LCDC_PID_REG),
 452                REG(1, true,  LCDC_CTRL_REG),
 453                REG(1, false, LCDC_STAT_REG),
 454                REG(1, true,  LCDC_RASTER_CTRL_REG),
 455                REG(1, true,  LCDC_RASTER_TIMING_0_REG),
 456                REG(1, true,  LCDC_RASTER_TIMING_1_REG),
 457                REG(1, true,  LCDC_RASTER_TIMING_2_REG),
 458                REG(1, true,  LCDC_DMA_CTRL_REG),
 459                REG(1, true,  LCDC_DMA_FB_BASE_ADDR_0_REG),
 460                REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_0_REG),
 461                REG(1, true,  LCDC_DMA_FB_BASE_ADDR_1_REG),
 462                REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_1_REG),
 463                /* new in revision 2: */
 464                REG(2, false, LCDC_RAW_STAT_REG),
 465                REG(2, false, LCDC_MASKED_STAT_REG),
 466                REG(2, true, LCDC_INT_ENABLE_SET_REG),
 467                REG(2, false, LCDC_INT_ENABLE_CLR_REG),
 468                REG(2, false, LCDC_END_OF_INT_IND_REG),
 469                REG(2, true,  LCDC_CLK_ENABLE_REG),
 470#undef REG
 471};
 472
 473#endif
 474
 475#ifdef CONFIG_DEBUG_FS
 476static int tilcdc_regs_show(struct seq_file *m, void *arg)
 477{
 478        struct drm_info_node *node = (struct drm_info_node *) m->private;
 479        struct drm_device *dev = node->minor->dev;
 480        struct tilcdc_drm_private *priv = dev->dev_private;
 481        unsigned i;
 482
 483        pm_runtime_get_sync(dev->dev);
 484
 485        seq_printf(m, "revision: %d\n", priv->rev);
 486
 487        for (i = 0; i < ARRAY_SIZE(registers); i++)
 488                if (priv->rev >= registers[i].rev)
 489                        seq_printf(m, "%s:\t %08x\n", registers[i].name,
 490                                        tilcdc_read(dev, registers[i].reg));
 491
 492        pm_runtime_put_sync(dev->dev);
 493
 494        return 0;
 495}
 496
 497static int tilcdc_mm_show(struct seq_file *m, void *arg)
 498{
 499        struct drm_info_node *node = (struct drm_info_node *) m->private;
 500        struct drm_device *dev = node->minor->dev;
 501        struct drm_printer p = drm_seq_file_printer(m);
 502        drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p);
 503        return 0;
 504}
 505
 506static struct drm_info_list tilcdc_debugfs_list[] = {
 507                { "regs", tilcdc_regs_show, 0 },
 508                { "mm",   tilcdc_mm_show,   0 },
 509                { "fb",   drm_fb_cma_debugfs_show, 0 },
 510};
 511
 512static int tilcdc_debugfs_init(struct drm_minor *minor)
 513{
 514        struct drm_device *dev = minor->dev;
 515        struct tilcdc_module *mod;
 516        int ret;
 517
 518        ret = drm_debugfs_create_files(tilcdc_debugfs_list,
 519                        ARRAY_SIZE(tilcdc_debugfs_list),
 520                        minor->debugfs_root, minor);
 521
 522        list_for_each_entry(mod, &module_list, list)
 523                if (mod->funcs->debugfs_init)
 524                        mod->funcs->debugfs_init(mod, minor);
 525
 526        if (ret) {
 527                dev_err(dev->dev, "could not install tilcdc_debugfs_list\n");
 528                return ret;
 529        }
 530
 531        return ret;
 532}
 533#endif
 534
 535DEFINE_DRM_GEM_CMA_FOPS(fops);
 536
 537static struct drm_driver tilcdc_driver = {
 538        .driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
 539                               DRIVER_PRIME | DRIVER_ATOMIC),
 540        .lastclose          = tilcdc_lastclose,
 541        .irq_handler        = tilcdc_irq,
 542        .gem_free_object_unlocked = drm_gem_cma_free_object,
 543        .gem_vm_ops         = &drm_gem_cma_vm_ops,
 544        .dumb_create        = drm_gem_cma_dumb_create,
 545
 546        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
 547        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
 548        .gem_prime_import       = drm_gem_prime_import,
 549        .gem_prime_export       = drm_gem_prime_export,
 550        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
 551        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
 552        .gem_prime_vmap         = drm_gem_cma_prime_vmap,
 553        .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
 554        .gem_prime_mmap         = drm_gem_cma_prime_mmap,
 555#ifdef CONFIG_DEBUG_FS
 556        .debugfs_init       = tilcdc_debugfs_init,
 557#endif
 558        .fops               = &fops,
 559        .name               = "tilcdc",
 560        .desc               = "TI LCD Controller DRM",
 561        .date               = "20121205",
 562        .major              = 1,
 563        .minor              = 0,
 564};
 565
 566/*
 567 * Power management:
 568 */
 569
 570#ifdef CONFIG_PM_SLEEP
 571static int tilcdc_pm_suspend(struct device *dev)
 572{
 573        struct drm_device *ddev = dev_get_drvdata(dev);
 574        struct tilcdc_drm_private *priv = ddev->dev_private;
 575
 576        priv->saved_state = drm_atomic_helper_suspend(ddev);
 577
 578        /* Select sleep pin state */
 579        pinctrl_pm_select_sleep_state(dev);
 580
 581        return 0;
 582}
 583
 584static int tilcdc_pm_resume(struct device *dev)
 585{
 586        struct drm_device *ddev = dev_get_drvdata(dev);
 587        struct tilcdc_drm_private *priv = ddev->dev_private;
 588        int ret = 0;
 589
 590        /* Select default pin state */
 591        pinctrl_pm_select_default_state(dev);
 592
 593        if (priv->saved_state)
 594                ret = drm_atomic_helper_resume(ddev, priv->saved_state);
 595
 596        return ret;
 597}
 598#endif
 599
 600static const struct dev_pm_ops tilcdc_pm_ops = {
 601        SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume)
 602};
 603
 604/*
 605 * Platform driver:
 606 */
 607static int tilcdc_bind(struct device *dev)
 608{
 609        return tilcdc_init(&tilcdc_driver, dev);
 610}
 611
 612static void tilcdc_unbind(struct device *dev)
 613{
 614        struct drm_device *ddev = dev_get_drvdata(dev);
 615
 616        /* Check if a subcomponent has already triggered the unloading. */
 617        if (!ddev->dev_private)
 618                return;
 619
 620        tilcdc_fini(dev_get_drvdata(dev));
 621}
 622
 623static const struct component_master_ops tilcdc_comp_ops = {
 624        .bind = tilcdc_bind,
 625        .unbind = tilcdc_unbind,
 626};
 627
 628static int tilcdc_pdev_probe(struct platform_device *pdev)
 629{
 630        struct component_match *match = NULL;
 631        int ret;
 632
 633        /* bail out early if no DT data: */
 634        if (!pdev->dev.of_node) {
 635                dev_err(&pdev->dev, "device-tree data is missing\n");
 636                return -ENXIO;
 637        }
 638
 639        ret = tilcdc_get_external_components(&pdev->dev, &match);
 640        if (ret < 0)
 641                return ret;
 642        else if (ret == 0)
 643                return tilcdc_init(&tilcdc_driver, &pdev->dev);
 644        else
 645                return component_master_add_with_match(&pdev->dev,
 646                                                       &tilcdc_comp_ops,
 647                                                       match);
 648}
 649
 650static int tilcdc_pdev_remove(struct platform_device *pdev)
 651{
 652        int ret;
 653
 654        ret = tilcdc_get_external_components(&pdev->dev, NULL);
 655        if (ret < 0)
 656                return ret;
 657        else if (ret == 0)
 658                tilcdc_fini(platform_get_drvdata(pdev));
 659        else
 660                component_master_del(&pdev->dev, &tilcdc_comp_ops);
 661
 662        return 0;
 663}
 664
 665static struct of_device_id tilcdc_of_match[] = {
 666                { .compatible = "ti,am33xx-tilcdc", },
 667                { .compatible = "ti,da850-tilcdc", },
 668                { },
 669};
 670MODULE_DEVICE_TABLE(of, tilcdc_of_match);
 671
 672static struct platform_driver tilcdc_platform_driver = {
 673        .probe      = tilcdc_pdev_probe,
 674        .remove     = tilcdc_pdev_remove,
 675        .driver     = {
 676                .name   = "tilcdc",
 677                .pm     = &tilcdc_pm_ops,
 678                .of_match_table = tilcdc_of_match,
 679        },
 680};
 681
 682static int __init tilcdc_drm_init(void)
 683{
 684        DBG("init");
 685        tilcdc_tfp410_init();
 686        tilcdc_panel_init();
 687        return platform_driver_register(&tilcdc_platform_driver);
 688}
 689
 690static void __exit tilcdc_drm_fini(void)
 691{
 692        DBG("fini");
 693        platform_driver_unregister(&tilcdc_platform_driver);
 694        tilcdc_panel_fini();
 695        tilcdc_tfp410_fini();
 696}
 697
 698module_init(tilcdc_drm_init);
 699module_exit(tilcdc_drm_fini);
 700
 701MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
 702MODULE_DESCRIPTION("TI LCD Controller DRM Driver");
 703MODULE_LICENSE("GPL");
 704