linux/drivers/video/omap2/dss/dpi.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/omap2/dss/dpi.c
   3 *
   4 * Copyright (C) 2009 Nokia Corporation
   5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
   6 *
   7 * Some code and ideas taken from drivers/video/omap/ driver
   8 * by Imre Deak.
   9 *
  10 * This program is free software; you can redistribute it and/or modify it
  11 * under the terms of the GNU General Public License version 2 as published by
  12 * the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful, but WITHOUT
  15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  17 * more details.
  18 *
  19 * You should have received a copy of the GNU General Public License along with
  20 * this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#define DSS_SUBSYS_NAME "DPI"
  24
  25#include <linux/kernel.h>
  26#include <linux/delay.h>
  27#include <linux/export.h>
  28#include <linux/err.h>
  29#include <linux/errno.h>
  30#include <linux/platform_device.h>
  31#include <linux/regulator/consumer.h>
  32#include <linux/string.h>
  33
  34#include <video/omapdss.h>
  35
  36#include "dss.h"
  37#include "dss_features.h"
  38
  39static struct {
  40        struct regulator *vdds_dsi_reg;
  41        struct platform_device *dsidev;
  42
  43        struct mutex lock;
  44
  45        struct omap_video_timings timings;
  46        struct dss_lcd_mgr_config mgr_config;
  47        int data_lines;
  48
  49        struct omap_dss_output output;
  50} dpi;
  51
  52static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
  53{
  54        /*
  55         * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
  56         * would also be used for DISPC fclk. Meaning, when the DPI output is
  57         * disabled, DISPC clock will be disabled, and TV out will stop.
  58         */
  59        switch (omapdss_get_version()) {
  60        case OMAPDSS_VER_OMAP24xx:
  61        case OMAPDSS_VER_OMAP34xx_ES1:
  62        case OMAPDSS_VER_OMAP34xx_ES3:
  63        case OMAPDSS_VER_OMAP3630:
  64        case OMAPDSS_VER_AM35xx:
  65                return NULL;
  66
  67        case OMAPDSS_VER_OMAP4430_ES1:
  68        case OMAPDSS_VER_OMAP4430_ES2:
  69        case OMAPDSS_VER_OMAP4:
  70                switch (channel) {
  71                case OMAP_DSS_CHANNEL_LCD:
  72                        return dsi_get_dsidev_from_id(0);
  73                case OMAP_DSS_CHANNEL_LCD2:
  74                        return dsi_get_dsidev_from_id(1);
  75                default:
  76                        return NULL;
  77                }
  78
  79        case OMAPDSS_VER_OMAP5:
  80                switch (channel) {
  81                case OMAP_DSS_CHANNEL_LCD:
  82                        return dsi_get_dsidev_from_id(0);
  83                case OMAP_DSS_CHANNEL_LCD3:
  84                        return dsi_get_dsidev_from_id(1);
  85                default:
  86                        return NULL;
  87                }
  88
  89        default:
  90                return NULL;
  91        }
  92}
  93
  94static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
  95{
  96        switch (channel) {
  97        case OMAP_DSS_CHANNEL_LCD:
  98                return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
  99        case OMAP_DSS_CHANNEL_LCD2:
 100                return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
 101        default:
 102                /* this shouldn't happen */
 103                WARN_ON(1);
 104                return OMAP_DSS_CLK_SRC_FCK;
 105        }
 106}
 107
 108struct dpi_clk_calc_ctx {
 109        struct platform_device *dsidev;
 110
 111        /* inputs */
 112
 113        unsigned long pck_min, pck_max;
 114
 115        /* outputs */
 116
 117        struct dsi_clock_info dsi_cinfo;
 118        struct dss_clock_info dss_cinfo;
 119        struct dispc_clock_info dispc_cinfo;
 120};
 121
 122static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
 123                unsigned long pck, void *data)
 124{
 125        struct dpi_clk_calc_ctx *ctx = data;
 126
 127        /*
 128         * Odd dividers give us uneven duty cycle, causing problem when level
 129         * shifted. So skip all odd dividers when the pixel clock is on the
 130         * higher side.
 131         */
 132        if (ctx->pck_min >= 1000000) {
 133                if (lckd > 1 && lckd % 2 != 0)
 134                        return false;
 135
 136                if (pckd > 1 && pckd % 2 != 0)
 137                        return false;
 138        }
 139
 140        ctx->dispc_cinfo.lck_div = lckd;
 141        ctx->dispc_cinfo.pck_div = pckd;
 142        ctx->dispc_cinfo.lck = lck;
 143        ctx->dispc_cinfo.pck = pck;
 144
 145        return true;
 146}
 147
 148
 149static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
 150                void *data)
 151{
 152        struct dpi_clk_calc_ctx *ctx = data;
 153
 154        /*
 155         * Odd dividers give us uneven duty cycle, causing problem when level
 156         * shifted. So skip all odd dividers when the pixel clock is on the
 157         * higher side.
 158         */
 159        if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 1000000)
 160                return false;
 161
 162        ctx->dsi_cinfo.regm_dispc = regm_dispc;
 163        ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
 164
 165        return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
 166                        dpi_calc_dispc_cb, ctx);
 167}
 168
 169
 170static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint,
 171                unsigned long pll,
 172                void *data)
 173{
 174        struct dpi_clk_calc_ctx *ctx = data;
 175
 176        ctx->dsi_cinfo.regn = regn;
 177        ctx->dsi_cinfo.regm = regm;
 178        ctx->dsi_cinfo.fint = fint;
 179        ctx->dsi_cinfo.clkin4ddr = pll;
 180
 181        return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min,
 182                        dpi_calc_hsdiv_cb, ctx);
 183}
 184
 185static bool dpi_calc_dss_cb(int fckd, unsigned long fck, void *data)
 186{
 187        struct dpi_clk_calc_ctx *ctx = data;
 188
 189        ctx->dss_cinfo.fck = fck;
 190        ctx->dss_cinfo.fck_div = fckd;
 191
 192        return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
 193                        dpi_calc_dispc_cb, ctx);
 194}
 195
 196static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
 197{
 198        unsigned long clkin;
 199        unsigned long pll_min, pll_max;
 200
 201        clkin = dsi_get_pll_clkin(dpi.dsidev);
 202
 203        memset(ctx, 0, sizeof(*ctx));
 204        ctx->dsidev = dpi.dsidev;
 205        ctx->pck_min = pck - 1000;
 206        ctx->pck_max = pck + 1000;
 207        ctx->dsi_cinfo.clkin = clkin;
 208
 209        pll_min = 0;
 210        pll_max = 0;
 211
 212        return dsi_pll_calc(dpi.dsidev, clkin,
 213                        pll_min, pll_max,
 214                        dpi_calc_pll_cb, ctx);
 215}
 216
 217static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
 218{
 219        int i;
 220
 221        /*
 222         * DSS fck gives us very few possibilities, so finding a good pixel
 223         * clock may not be possible. We try multiple times to find the clock,
 224         * each time widening the pixel clock range we look for, up to
 225         * +/- ~15MHz.
 226         */
 227
 228        for (i = 0; i < 25; ++i) {
 229                bool ok;
 230
 231                memset(ctx, 0, sizeof(*ctx));
 232                if (pck > 1000 * i * i * i)
 233                        ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
 234                else
 235                        ctx->pck_min = 0;
 236                ctx->pck_max = pck + 1000 * i * i * i;
 237
 238                ok = dss_div_calc(ctx->pck_min, dpi_calc_dss_cb, ctx);
 239                if (ok)
 240                        return ok;
 241        }
 242
 243        return false;
 244}
 245
 246
 247
 248static int dpi_set_dsi_clk(enum omap_channel channel,
 249                unsigned long pck_req, unsigned long *fck, int *lck_div,
 250                int *pck_div)
 251{
 252        struct dpi_clk_calc_ctx ctx;
 253        int r;
 254        bool ok;
 255
 256        ok = dpi_dsi_clk_calc(pck_req, &ctx);
 257        if (!ok)
 258                return -EINVAL;
 259
 260        r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo);
 261        if (r)
 262                return r;
 263
 264        dss_select_lcd_clk_source(channel,
 265                        dpi_get_alt_clk_src(channel));
 266
 267        dpi.mgr_config.clock_info = ctx.dispc_cinfo;
 268
 269        *fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
 270        *lck_div = ctx.dispc_cinfo.lck_div;
 271        *pck_div = ctx.dispc_cinfo.pck_div;
 272
 273        return 0;
 274}
 275
 276static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck,
 277                int *lck_div, int *pck_div)
 278{
 279        struct dpi_clk_calc_ctx ctx;
 280        int r;
 281        bool ok;
 282
 283        ok = dpi_dss_clk_calc(pck_req, &ctx);
 284        if (!ok)
 285                return -EINVAL;
 286
 287        r = dss_set_clock_div(&ctx.dss_cinfo);
 288        if (r)
 289                return r;
 290
 291        dpi.mgr_config.clock_info = ctx.dispc_cinfo;
 292
 293        *fck = ctx.dss_cinfo.fck;
 294        *lck_div = ctx.dispc_cinfo.lck_div;
 295        *pck_div = ctx.dispc_cinfo.pck_div;
 296
 297        return 0;
 298}
 299
 300static int dpi_set_mode(struct omap_overlay_manager *mgr)
 301{
 302        struct omap_video_timings *t = &dpi.timings;
 303        int lck_div = 0, pck_div = 0;
 304        unsigned long fck = 0;
 305        unsigned long pck;
 306        int r = 0;
 307
 308        if (dpi.dsidev)
 309                r = dpi_set_dsi_clk(mgr->id, t->pixel_clock * 1000, &fck,
 310                                &lck_div, &pck_div);
 311        else
 312                r = dpi_set_dispc_clk(t->pixel_clock * 1000, &fck,
 313                                &lck_div, &pck_div);
 314        if (r)
 315                return r;
 316
 317        pck = fck / lck_div / pck_div / 1000;
 318
 319        if (pck != t->pixel_clock) {
 320                DSSWARN("Could not find exact pixel clock. "
 321                                "Requested %d kHz, got %lu kHz\n",
 322                                t->pixel_clock, pck);
 323
 324                t->pixel_clock = pck;
 325        }
 326
 327        dss_mgr_set_timings(mgr, t);
 328
 329        return 0;
 330}
 331
 332static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr)
 333{
 334        dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
 335
 336        dpi.mgr_config.stallmode = false;
 337        dpi.mgr_config.fifohandcheck = false;
 338
 339        dpi.mgr_config.video_port_width = dpi.data_lines;
 340
 341        dpi.mgr_config.lcden_sig_polarity = 0;
 342
 343        dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
 344}
 345
 346int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
 347{
 348        struct omap_dss_output *out = &dpi.output;
 349        int r;
 350
 351        mutex_lock(&dpi.lock);
 352
 353        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
 354                DSSERR("no VDSS_DSI regulator\n");
 355                r = -ENODEV;
 356                goto err_no_reg;
 357        }
 358
 359        if (out == NULL || out->manager == NULL) {
 360                DSSERR("failed to enable display: no output/manager\n");
 361                r = -ENODEV;
 362                goto err_no_out_mgr;
 363        }
 364
 365        r = omap_dss_start_device(dssdev);
 366        if (r) {
 367                DSSERR("failed to start device\n");
 368                goto err_start_dev;
 369        }
 370
 371        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
 372                r = regulator_enable(dpi.vdds_dsi_reg);
 373                if (r)
 374                        goto err_reg_enable;
 375        }
 376
 377        r = dispc_runtime_get();
 378        if (r)
 379                goto err_get_dispc;
 380
 381        r = dss_dpi_select_source(out->manager->id);
 382        if (r)
 383                goto err_src_sel;
 384
 385        if (dpi.dsidev) {
 386                r = dsi_runtime_get(dpi.dsidev);
 387                if (r)
 388                        goto err_get_dsi;
 389
 390                r = dsi_pll_init(dpi.dsidev, 0, 1);
 391                if (r)
 392                        goto err_dsi_pll_init;
 393        }
 394
 395        r = dpi_set_mode(out->manager);
 396        if (r)
 397                goto err_set_mode;
 398
 399        dpi_config_lcd_manager(out->manager);
 400
 401        mdelay(2);
 402
 403        r = dss_mgr_enable(out->manager);
 404        if (r)
 405                goto err_mgr_enable;
 406
 407        mutex_unlock(&dpi.lock);
 408
 409        return 0;
 410
 411err_mgr_enable:
 412err_set_mode:
 413        if (dpi.dsidev)
 414                dsi_pll_uninit(dpi.dsidev, true);
 415err_dsi_pll_init:
 416        if (dpi.dsidev)
 417                dsi_runtime_put(dpi.dsidev);
 418err_get_dsi:
 419err_src_sel:
 420        dispc_runtime_put();
 421err_get_dispc:
 422        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 423                regulator_disable(dpi.vdds_dsi_reg);
 424err_reg_enable:
 425        omap_dss_stop_device(dssdev);
 426err_start_dev:
 427err_no_out_mgr:
 428err_no_reg:
 429        mutex_unlock(&dpi.lock);
 430        return r;
 431}
 432EXPORT_SYMBOL(omapdss_dpi_display_enable);
 433
 434void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
 435{
 436        struct omap_overlay_manager *mgr = dpi.output.manager;
 437
 438        mutex_lock(&dpi.lock);
 439
 440        dss_mgr_disable(mgr);
 441
 442        if (dpi.dsidev) {
 443                dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
 444                dsi_pll_uninit(dpi.dsidev, true);
 445                dsi_runtime_put(dpi.dsidev);
 446        }
 447
 448        dispc_runtime_put();
 449
 450        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 451                regulator_disable(dpi.vdds_dsi_reg);
 452
 453        omap_dss_stop_device(dssdev);
 454
 455        mutex_unlock(&dpi.lock);
 456}
 457EXPORT_SYMBOL(omapdss_dpi_display_disable);
 458
 459void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
 460                struct omap_video_timings *timings)
 461{
 462        DSSDBG("dpi_set_timings\n");
 463
 464        mutex_lock(&dpi.lock);
 465
 466        dpi.timings = *timings;
 467
 468        mutex_unlock(&dpi.lock);
 469}
 470EXPORT_SYMBOL(omapdss_dpi_set_timings);
 471
 472int dpi_check_timings(struct omap_dss_device *dssdev,
 473                        struct omap_video_timings *timings)
 474{
 475        struct omap_overlay_manager *mgr = dpi.output.manager;
 476        int lck_div, pck_div;
 477        unsigned long fck;
 478        unsigned long pck;
 479        struct dpi_clk_calc_ctx ctx;
 480        bool ok;
 481
 482        if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
 483                return -EINVAL;
 484
 485        if (timings->pixel_clock == 0)
 486                return -EINVAL;
 487
 488        if (dpi.dsidev) {
 489                ok = dpi_dsi_clk_calc(timings->pixel_clock * 1000, &ctx);
 490                if (!ok)
 491                        return -EINVAL;
 492
 493                fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
 494        } else {
 495                ok = dpi_dss_clk_calc(timings->pixel_clock * 1000, &ctx);
 496                if (!ok)
 497                        return -EINVAL;
 498
 499                fck = ctx.dss_cinfo.fck;
 500        }
 501
 502        lck_div = ctx.dispc_cinfo.lck_div;
 503        pck_div = ctx.dispc_cinfo.pck_div;
 504
 505        pck = fck / lck_div / pck_div / 1000;
 506
 507        timings->pixel_clock = pck;
 508
 509        return 0;
 510}
 511EXPORT_SYMBOL(dpi_check_timings);
 512
 513void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
 514{
 515        mutex_lock(&dpi.lock);
 516
 517        dpi.data_lines = data_lines;
 518
 519        mutex_unlock(&dpi.lock);
 520}
 521EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
 522
 523static int dpi_verify_dsi_pll(struct platform_device *dsidev)
 524{
 525        int r;
 526
 527        /* do initial setup with the PLL to see if it is operational */
 528
 529        r = dsi_runtime_get(dsidev);
 530        if (r)
 531                return r;
 532
 533        r = dsi_pll_init(dsidev, 0, 1);
 534        if (r) {
 535                dsi_runtime_put(dsidev);
 536                return r;
 537        }
 538
 539        dsi_pll_uninit(dsidev, true);
 540        dsi_runtime_put(dsidev);
 541
 542        return 0;
 543}
 544
 545/*
 546 * Return a hardcoded channel for the DPI output. This should work for
 547 * current use cases, but this can be later expanded to either resolve
 548 * the channel in some more dynamic manner, or get the channel as a user
 549 * parameter.
 550 */
 551static enum omap_channel dpi_get_channel(void)
 552{
 553        switch (omapdss_get_version()) {
 554        case OMAPDSS_VER_OMAP24xx:
 555        case OMAPDSS_VER_OMAP34xx_ES1:
 556        case OMAPDSS_VER_OMAP34xx_ES3:
 557        case OMAPDSS_VER_OMAP3630:
 558        case OMAPDSS_VER_AM35xx:
 559                return OMAP_DSS_CHANNEL_LCD;
 560
 561        case OMAPDSS_VER_OMAP4430_ES1:
 562        case OMAPDSS_VER_OMAP4430_ES2:
 563        case OMAPDSS_VER_OMAP4:
 564                return OMAP_DSS_CHANNEL_LCD2;
 565
 566        case OMAPDSS_VER_OMAP5:
 567                return OMAP_DSS_CHANNEL_LCD3;
 568
 569        default:
 570                DSSWARN("unsupported DSS version\n");
 571                return OMAP_DSS_CHANNEL_LCD;
 572        }
 573}
 574
 575static int dpi_init_display(struct omap_dss_device *dssdev)
 576{
 577        struct platform_device *dsidev;
 578
 579        DSSDBG("init_display\n");
 580
 581        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
 582                                        dpi.vdds_dsi_reg == NULL) {
 583                struct regulator *vdds_dsi;
 584
 585                vdds_dsi = dss_get_vdds_dsi();
 586
 587                if (IS_ERR(vdds_dsi)) {
 588                        DSSERR("can't get VDDS_DSI regulator\n");
 589                        return PTR_ERR(vdds_dsi);
 590                }
 591
 592                dpi.vdds_dsi_reg = vdds_dsi;
 593        }
 594
 595        dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
 596
 597        if (dsidev && dpi_verify_dsi_pll(dsidev)) {
 598                dsidev = NULL;
 599                DSSWARN("DSI PLL not operational\n");
 600        }
 601
 602        if (dsidev)
 603                DSSDBG("using DSI PLL for DPI clock\n");
 604
 605        dpi.dsidev = dsidev;
 606
 607        return 0;
 608}
 609
 610static struct omap_dss_device *dpi_find_dssdev(struct platform_device *pdev)
 611{
 612        struct omap_dss_board_info *pdata = pdev->dev.platform_data;
 613        const char *def_disp_name = omapdss_get_default_display_name();
 614        struct omap_dss_device *def_dssdev;
 615        int i;
 616
 617        def_dssdev = NULL;
 618
 619        for (i = 0; i < pdata->num_devices; ++i) {
 620                struct omap_dss_device *dssdev = pdata->devices[i];
 621
 622                if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
 623                        continue;
 624
 625                if (def_dssdev == NULL)
 626                        def_dssdev = dssdev;
 627
 628                if (def_disp_name != NULL &&
 629                                strcmp(dssdev->name, def_disp_name) == 0) {
 630                        def_dssdev = dssdev;
 631                        break;
 632                }
 633        }
 634
 635        return def_dssdev;
 636}
 637
 638static int dpi_probe_pdata(struct platform_device *dpidev)
 639{
 640        struct omap_dss_device *plat_dssdev;
 641        struct omap_dss_device *dssdev;
 642        int r;
 643
 644        plat_dssdev = dpi_find_dssdev(dpidev);
 645
 646        if (!plat_dssdev)
 647                return 0;
 648
 649        dssdev = dss_alloc_and_init_device(&dpidev->dev);
 650        if (!dssdev)
 651                return -ENOMEM;
 652
 653        dss_copy_device_pdata(dssdev, plat_dssdev);
 654
 655        r = dpi_init_display(dssdev);
 656        if (r) {
 657                DSSERR("device %s init failed: %d\n", dssdev->name, r);
 658                dss_put_device(dssdev);
 659                return r;
 660        }
 661
 662        r = omapdss_output_set_device(&dpi.output, dssdev);
 663        if (r) {
 664                DSSERR("failed to connect output to new device: %s\n",
 665                                dssdev->name);
 666                dss_put_device(dssdev);
 667                return r;
 668        }
 669
 670        r = dss_add_device(dssdev);
 671        if (r) {
 672                DSSERR("device %s register failed: %d\n", dssdev->name, r);
 673                omapdss_output_unset_device(&dpi.output);
 674                dss_put_device(dssdev);
 675                return r;
 676        }
 677
 678        return 0;
 679}
 680
 681static void dpi_init_output(struct platform_device *pdev)
 682{
 683        struct omap_dss_output *out = &dpi.output;
 684
 685        out->pdev = pdev;
 686        out->id = OMAP_DSS_OUTPUT_DPI;
 687        out->type = OMAP_DISPLAY_TYPE_DPI;
 688        out->name = "dpi.0";
 689        out->dispc_channel = dpi_get_channel();
 690
 691        dss_register_output(out);
 692}
 693
 694static void __exit dpi_uninit_output(struct platform_device *pdev)
 695{
 696        struct omap_dss_output *out = &dpi.output;
 697
 698        dss_unregister_output(out);
 699}
 700
 701static int omap_dpi_probe(struct platform_device *pdev)
 702{
 703        int r;
 704
 705        mutex_init(&dpi.lock);
 706
 707        dpi_init_output(pdev);
 708
 709        r = dpi_probe_pdata(pdev);
 710        if (r) {
 711                dpi_uninit_output(pdev);
 712                return r;
 713        }
 714
 715        return 0;
 716}
 717
 718static int __exit omap_dpi_remove(struct platform_device *pdev)
 719{
 720        dss_unregister_child_devices(&pdev->dev);
 721
 722        dpi_uninit_output(pdev);
 723
 724        return 0;
 725}
 726
 727static struct platform_driver omap_dpi_driver = {
 728        .probe          = omap_dpi_probe,
 729        .remove         = __exit_p(omap_dpi_remove),
 730        .driver         = {
 731                .name   = "omapdss_dpi",
 732                .owner  = THIS_MODULE,
 733        },
 734};
 735
 736int __init dpi_init_platform_driver(void)
 737{
 738        return platform_driver_register(&omap_dpi_driver);
 739}
 740
 741void __exit dpi_uninit_platform_driver(void)
 742{
 743        platform_driver_unregister(&omap_dpi_driver);
 744}
 745