linux/drivers/video/omap2/displays/panel-generic-dpi.c
<<
>>
Prefs
   1/*
   2 * Generic DPI Panels support
   3 *
   4 * Copyright (C) 2010 Canonical Ltd.
   5 * Author: Bryan Wu <bryan.wu@canonical.com>
   6 *
   7 * LCD panel driver for Sharp LQ043T1DG01
   8 *
   9 * Copyright (C) 2009 Texas Instruments Inc
  10 * Author: Vaibhav Hiremath <hvaibhav@ti.com>
  11 *
  12 * LCD panel driver for Toppoly TDO35S
  13 *
  14 * Copyright (C) 2009 CompuLab, Ltd.
  15 * Author: Mike Rapoport <mike@compulab.co.il>
  16 *
  17 * Copyright (C) 2008 Nokia Corporation
  18 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
  19 *
  20 * This program is free software; you can redistribute it and/or modify it
  21 * under the terms of the GNU General Public License version 2 as published by
  22 * the Free Software Foundation.
  23 *
  24 * This program is distributed in the hope that it will be useful, but WITHOUT
  25 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  26 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  27 * more details.
  28 *
  29 * You should have received a copy of the GNU General Public License along with
  30 * this program.  If not, see <http://www.gnu.org/licenses/>.
  31 */
  32
  33#include <linux/module.h>
  34#include <linux/delay.h>
  35#include <linux/slab.h>
  36#include <video/omapdss.h>
  37
  38#include <video/omap-panel-generic-dpi.h>
  39
  40struct panel_config {
  41        struct omap_video_timings timings;
  42
  43        int power_on_delay;
  44        int power_off_delay;
  45
  46        /*
  47         * Used to match device to panel configuration
  48         * when use generic panel driver
  49         */
  50        const char *name;
  51};
  52
  53/* Panel configurations */
  54static struct panel_config generic_dpi_panels[] = {
  55        /* Sharp LQ043T1DG01 */
  56        {
  57                {
  58                        .x_res          = 480,
  59                        .y_res          = 272,
  60
  61                        .pixel_clock    = 9000,
  62
  63                        .hsw            = 42,
  64                        .hfp            = 3,
  65                        .hbp            = 2,
  66
  67                        .vsw            = 11,
  68                        .vfp            = 3,
  69                        .vbp            = 2,
  70
  71                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  72                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  73                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
  74                        .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
  75                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
  76                },
  77                .power_on_delay         = 50,
  78                .power_off_delay        = 100,
  79                .name                   = "sharp_lq",
  80        },
  81
  82        /* Sharp LS037V7DW01 */
  83        {
  84                {
  85                        .x_res          = 480,
  86                        .y_res          = 640,
  87
  88                        .pixel_clock    = 19200,
  89
  90                        .hsw            = 2,
  91                        .hfp            = 1,
  92                        .hbp            = 28,
  93
  94                        .vsw            = 1,
  95                        .vfp            = 1,
  96                        .vbp            = 1,
  97
  98                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  99                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 100                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 101                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 102                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 103                },
 104                .power_on_delay         = 50,
 105                .power_off_delay        = 100,
 106                .name                   = "sharp_ls",
 107        },
 108
 109        /* Toppoly TDO35S */
 110        {
 111                {
 112                        .x_res          = 480,
 113                        .y_res          = 640,
 114
 115                        .pixel_clock    = 26000,
 116
 117                        .hfp            = 104,
 118                        .hsw            = 8,
 119                        .hbp            = 8,
 120
 121                        .vfp            = 4,
 122                        .vsw            = 2,
 123                        .vbp            = 2,
 124
 125                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 126                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 127                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 128                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 129                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 130                },
 131                .power_on_delay         = 0,
 132                .power_off_delay        = 0,
 133                .name                   = "toppoly_tdo35s",
 134        },
 135
 136        /* Samsung LTE430WQ-F0C */
 137        {
 138                {
 139                        .x_res          = 480,
 140                        .y_res          = 272,
 141
 142                        .pixel_clock    = 9200,
 143
 144                        .hfp            = 8,
 145                        .hsw            = 41,
 146                        .hbp            = 45 - 41,
 147
 148                        .vfp            = 4,
 149                        .vsw            = 10,
 150                        .vbp            = 12 - 10,
 151
 152                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 153                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 154                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 155                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 156                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 157                },
 158                .power_on_delay         = 0,
 159                .power_off_delay        = 0,
 160                .name                   = "samsung_lte430wq_f0c",
 161        },
 162
 163        /* Seiko 70WVW1TZ3Z3 */
 164        {
 165                {
 166                        .x_res          = 800,
 167                        .y_res          = 480,
 168
 169                        .pixel_clock    = 33000,
 170
 171                        .hsw            = 128,
 172                        .hfp            = 10,
 173                        .hbp            = 10,
 174
 175                        .vsw            = 2,
 176                        .vfp            = 4,
 177                        .vbp            = 11,
 178
 179                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 180                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 181                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 182                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 183                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 184                },
 185                .power_on_delay         = 0,
 186                .power_off_delay        = 0,
 187                .name                   = "seiko_70wvw1tz3",
 188        },
 189
 190        /* Powertip PH480272T */
 191        {
 192                {
 193                        .x_res          = 480,
 194                        .y_res          = 272,
 195
 196                        .pixel_clock    = 9000,
 197
 198                        .hsw            = 40,
 199                        .hfp            = 2,
 200                        .hbp            = 2,
 201
 202                        .vsw            = 10,
 203                        .vfp            = 2,
 204                        .vbp            = 2,
 205
 206                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 207                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 208                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 209                        .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
 210                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 211                },
 212                .power_on_delay         = 0,
 213                .power_off_delay        = 0,
 214                .name                   = "powertip_ph480272t",
 215        },
 216
 217        /* Innolux AT070TN83 */
 218        {
 219                {
 220                        .x_res          = 800,
 221                        .y_res          = 480,
 222
 223                        .pixel_clock    = 40000,
 224
 225                        .hsw            = 48,
 226                        .hfp            = 1,
 227                        .hbp            = 1,
 228
 229                        .vsw            = 3,
 230                        .vfp            = 12,
 231                        .vbp            = 25,
 232
 233                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 234                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 235                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 236                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 237                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 238                },
 239                .power_on_delay         = 0,
 240                .power_off_delay        = 0,
 241                .name                   = "innolux_at070tn83",
 242        },
 243
 244        /* NEC NL2432DR22-11B */
 245        {
 246                {
 247                        .x_res          = 240,
 248                        .y_res          = 320,
 249
 250                        .pixel_clock    = 5400,
 251
 252                        .hsw            = 3,
 253                        .hfp            = 3,
 254                        .hbp            = 39,
 255
 256                        .vsw            = 1,
 257                        .vfp            = 2,
 258                        .vbp            = 7,
 259
 260                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 261                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 262                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 263                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 264                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 265                },
 266                .name                   = "nec_nl2432dr22-11b",
 267        },
 268
 269        /* Unknown panel used in OMAP H4 */
 270        {
 271                {
 272                        .x_res          = 240,
 273                        .y_res          = 320,
 274
 275                        .pixel_clock    = 6250,
 276
 277                        .hsw            = 15,
 278                        .hfp            = 15,
 279                        .hbp            = 60,
 280
 281                        .vsw            = 1,
 282                        .vfp            = 1,
 283                        .vbp            = 1,
 284
 285                        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 286                        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 287                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 288                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 289                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 290                },
 291                .name                   = "h4",
 292        },
 293
 294        /* Unknown panel used in Samsung OMAP2 Apollon */
 295        {
 296                {
 297                        .x_res          = 480,
 298                        .y_res          = 272,
 299
 300                        .pixel_clock    = 6250,
 301
 302                        .hsw            = 41,
 303                        .hfp            = 2,
 304                        .hbp            = 2,
 305
 306                        .vsw            = 10,
 307                        .vfp            = 2,
 308                        .vbp            = 2,
 309
 310                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 311                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 312                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 313                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 314                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 315                },
 316                .name                   = "apollon",
 317        },
 318        /* FocalTech ETM070003DH6 */
 319        {
 320                {
 321                        .x_res          = 800,
 322                        .y_res          = 480,
 323
 324                        .pixel_clock    = 28000,
 325
 326                        .hsw            = 48,
 327                        .hfp            = 40,
 328                        .hbp            = 40,
 329
 330                        .vsw            = 3,
 331                        .vfp            = 13,
 332                        .vbp            = 29,
 333
 334                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 335                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 336                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 337                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 338                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 339                },
 340                .name                   = "focaltech_etm070003dh6",
 341        },
 342
 343        /* Microtips Technologies - UMSH-8173MD */
 344        {
 345                {
 346                        .x_res          = 800,
 347                        .y_res          = 480,
 348
 349                        .pixel_clock    = 34560,
 350
 351                        .hsw            = 13,
 352                        .hfp            = 101,
 353                        .hbp            = 101,
 354
 355                        .vsw            = 23,
 356                        .vfp            = 1,
 357                        .vbp            = 1,
 358
 359                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 360                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 361                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 362                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 363                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 364                },
 365                .power_on_delay         = 0,
 366                .power_off_delay        = 0,
 367                .name                   = "microtips_umsh_8173md",
 368        },
 369
 370        /* OrtusTech COM43H4M10XTC */
 371        {
 372                {
 373                        .x_res          = 480,
 374                        .y_res          = 272,
 375
 376                        .pixel_clock    = 8000,
 377
 378                        .hsw            = 41,
 379                        .hfp            = 8,
 380                        .hbp            = 4,
 381
 382                        .vsw            = 10,
 383                        .vfp            = 4,
 384                        .vbp            = 2,
 385
 386                        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 387                        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 388                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 389                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 390                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 391                },
 392                .name                   = "ortustech_com43h4m10xtc",
 393        },
 394
 395        /* Innolux AT080TN52 */
 396        {
 397                {
 398                        .x_res = 800,
 399                        .y_res = 600,
 400
 401                        .pixel_clock    = 41142,
 402
 403                        .hsw            = 20,
 404                        .hfp            = 210,
 405                        .hbp            = 46,
 406
 407                        .vsw            = 10,
 408                        .vfp            = 12,
 409                        .vbp            = 23,
 410
 411                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 412                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 413                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 414                        .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
 415                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 416                },
 417                .name                   = "innolux_at080tn52",
 418        },
 419
 420        /* Mitsubishi AA084SB01 */
 421        {
 422                {
 423                        .x_res          = 800,
 424                        .y_res          = 600,
 425                        .pixel_clock    = 40000,
 426
 427                        .hsw            = 1,
 428                        .hfp            = 254,
 429                        .hbp            = 1,
 430
 431                        .vsw            = 1,
 432                        .vfp            = 26,
 433                        .vbp            = 1,
 434
 435                        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 436                        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 437                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 438                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 439                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 440                },
 441                .name                   = "mitsubishi_aa084sb01",
 442        },
 443        /* EDT ET0500G0DH6 */
 444        {
 445                {
 446                        .x_res          = 800,
 447                        .y_res          = 480,
 448                        .pixel_clock    = 33260,
 449
 450                        .hsw            = 128,
 451                        .hfp            = 216,
 452                        .hbp            = 40,
 453
 454                        .vsw            = 2,
 455                        .vfp            = 35,
 456                        .vbp            = 10,
 457
 458                        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 459                        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
 460                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 461                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 462                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 463                },
 464                .name                   = "edt_et0500g0dh6",
 465        },
 466
 467        /* Prime-View PD050VL1 */
 468        {
 469                {
 470                        .x_res          = 640,
 471                        .y_res          = 480,
 472
 473                        .pixel_clock    = 25000,
 474
 475                        .hsw            = 96,
 476                        .hfp            = 18,
 477                        .hbp            = 46,
 478
 479                        .vsw            = 2,
 480                        .vfp            = 10,
 481                        .vbp            = 33,
 482
 483                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 484                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 485                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 486                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 487                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 488                },
 489                .name                   = "primeview_pd050vl1",
 490        },
 491
 492        /* Prime-View PM070WL4 */
 493        {
 494                {
 495                        .x_res          = 800,
 496                        .y_res          = 480,
 497
 498                        .pixel_clock    = 32000,
 499
 500                        .hsw            = 128,
 501                        .hfp            = 42,
 502                        .hbp            = 86,
 503
 504                        .vsw            = 2,
 505                        .vfp            = 10,
 506                        .vbp            = 33,
 507
 508                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 509                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 510                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 511                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 512                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 513                },
 514                .name                   = "primeview_pm070wl4",
 515        },
 516
 517        /* Prime-View PD104SLF */
 518        {
 519                {
 520                        .x_res          = 800,
 521                        .y_res          = 600,
 522
 523                        .pixel_clock    = 40000,
 524
 525                        .hsw            = 128,
 526                        .hfp            = 42,
 527                        .hbp            = 86,
 528
 529                        .vsw            = 4,
 530                        .vfp            = 1,
 531                        .vbp            = 23,
 532
 533                        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 534                        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
 535                        .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 536                        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
 537                        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 538                },
 539                .name                   = "primeview_pd104slf",
 540        },
 541};
 542
 543struct panel_drv_data {
 544
 545        struct omap_dss_device *dssdev;
 546
 547        struct panel_config *panel_config;
 548
 549        struct mutex lock;
 550};
 551
 552static inline struct panel_generic_dpi_data
 553*get_panel_data(const struct omap_dss_device *dssdev)
 554{
 555        return (struct panel_generic_dpi_data *) dssdev->data;
 556}
 557
 558static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
 559{
 560        int r;
 561        struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
 562        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 563        struct panel_config *panel_config = drv_data->panel_config;
 564
 565        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
 566                return 0;
 567
 568        omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
 569        omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
 570
 571        r = omapdss_dpi_display_enable(dssdev);
 572        if (r)
 573                goto err0;
 574
 575        /* wait couple of vsyncs until enabling the LCD */
 576        if (panel_config->power_on_delay)
 577                msleep(panel_config->power_on_delay);
 578
 579        if (panel_data->platform_enable) {
 580                r = panel_data->platform_enable(dssdev);
 581                if (r)
 582                        goto err1;
 583        }
 584
 585        return 0;
 586err1:
 587        omapdss_dpi_display_disable(dssdev);
 588err0:
 589        return r;
 590}
 591
 592static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
 593{
 594        struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
 595        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 596        struct panel_config *panel_config = drv_data->panel_config;
 597
 598        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
 599                return;
 600
 601        if (panel_data->platform_disable)
 602                panel_data->platform_disable(dssdev);
 603
 604        /* wait couple of vsyncs after disabling the LCD */
 605        if (panel_config->power_off_delay)
 606                msleep(panel_config->power_off_delay);
 607
 608        omapdss_dpi_display_disable(dssdev);
 609}
 610
 611static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
 612{
 613        struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
 614        struct panel_config *panel_config = NULL;
 615        struct panel_drv_data *drv_data = NULL;
 616        int i;
 617
 618        dev_dbg(&dssdev->dev, "probe\n");
 619
 620        if (!panel_data || !panel_data->name)
 621                return -EINVAL;
 622
 623        for (i = 0; i < ARRAY_SIZE(generic_dpi_panels); i++) {
 624                if (strcmp(panel_data->name, generic_dpi_panels[i].name) == 0) {
 625                        panel_config = &generic_dpi_panels[i];
 626                        break;
 627                }
 628        }
 629
 630        if (!panel_config)
 631                return -EINVAL;
 632
 633        dssdev->panel.timings = panel_config->timings;
 634
 635        drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
 636        if (!drv_data)
 637                return -ENOMEM;
 638
 639        drv_data->dssdev = dssdev;
 640        drv_data->panel_config = panel_config;
 641
 642        mutex_init(&drv_data->lock);
 643
 644        dev_set_drvdata(&dssdev->dev, drv_data);
 645
 646        return 0;
 647}
 648
 649static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
 650{
 651        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 652
 653        dev_dbg(&dssdev->dev, "remove\n");
 654
 655        kfree(drv_data);
 656
 657        dev_set_drvdata(&dssdev->dev, NULL);
 658}
 659
 660static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
 661{
 662        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 663        int r;
 664
 665        mutex_lock(&drv_data->lock);
 666
 667        r = generic_dpi_panel_power_on(dssdev);
 668        if (r)
 669                goto err;
 670
 671        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 672err:
 673        mutex_unlock(&drv_data->lock);
 674
 675        return r;
 676}
 677
 678static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
 679{
 680        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 681
 682        mutex_lock(&drv_data->lock);
 683
 684        generic_dpi_panel_power_off(dssdev);
 685
 686        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 687
 688        mutex_unlock(&drv_data->lock);
 689}
 690
 691static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
 692                struct omap_video_timings *timings)
 693{
 694        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 695
 696        mutex_lock(&drv_data->lock);
 697
 698        omapdss_dpi_set_timings(dssdev, timings);
 699
 700        dssdev->panel.timings = *timings;
 701
 702        mutex_unlock(&drv_data->lock);
 703}
 704
 705static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
 706                struct omap_video_timings *timings)
 707{
 708        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 709
 710        mutex_lock(&drv_data->lock);
 711
 712        *timings = dssdev->panel.timings;
 713
 714        mutex_unlock(&drv_data->lock);
 715}
 716
 717static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
 718                struct omap_video_timings *timings)
 719{
 720        struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
 721        int r;
 722
 723        mutex_lock(&drv_data->lock);
 724
 725        r = dpi_check_timings(dssdev, timings);
 726
 727        mutex_unlock(&drv_data->lock);
 728
 729        return r;
 730}
 731
 732static struct omap_dss_driver dpi_driver = {
 733        .probe          = generic_dpi_panel_probe,
 734        .remove         = __exit_p(generic_dpi_panel_remove),
 735
 736        .enable         = generic_dpi_panel_enable,
 737        .disable        = generic_dpi_panel_disable,
 738
 739        .set_timings    = generic_dpi_panel_set_timings,
 740        .get_timings    = generic_dpi_panel_get_timings,
 741        .check_timings  = generic_dpi_panel_check_timings,
 742
 743        .driver         = {
 744                .name   = "generic_dpi_panel",
 745                .owner  = THIS_MODULE,
 746        },
 747};
 748
 749static int __init generic_dpi_panel_drv_init(void)
 750{
 751        return omap_dss_register_driver(&dpi_driver);
 752}
 753
 754static void __exit generic_dpi_panel_drv_exit(void)
 755{
 756        omap_dss_unregister_driver(&dpi_driver);
 757}
 758
 759module_init(generic_dpi_panel_drv_init);
 760module_exit(generic_dpi_panel_drv_exit);
 761MODULE_LICENSE("GPL");
 762