linux/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) STMicroelectronics SA 2017
   4 *
   5 * Authors: Philippe Cornu <philippe.cornu@st.com>
   6 *          Yannick Fertre <yannick.fertre@st.com>
   7 */
   8
   9#include <linux/backlight.h>
  10#include <linux/delay.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/module.h>
  13#include <linux/regulator/consumer.h>
  14
  15#include <video/mipi_display.h>
  16
  17#include <drm/drm_mipi_dsi.h>
  18#include <drm/drm_modes.h>
  19#include <drm/drm_panel.h>
  20
  21#define OTM8009A_BACKLIGHT_DEFAULT      240
  22#define OTM8009A_BACKLIGHT_MAX          255
  23
  24/* Manufacturer Command Set */
  25#define MCS_ADRSFT      0x0000  /* Address Shift Function */
  26#define MCS_PANSET      0xB3A6  /* Panel Type Setting */
  27#define MCS_SD_CTRL     0xC0A2  /* Source Driver Timing Setting */
  28#define MCS_P_DRV_M     0xC0B4  /* Panel Driving Mode */
  29#define MCS_OSC_ADJ     0xC181  /* Oscillator Adjustment for Idle/Normal mode */
  30#define MCS_RGB_VID_SET 0xC1A1  /* RGB Video Mode Setting */
  31#define MCS_SD_PCH_CTRL 0xC480  /* Source Driver Precharge Control */
  32#define MCS_NO_DOC1     0xC48A  /* Command not documented */
  33#define MCS_PWR_CTRL1   0xC580  /* Power Control Setting 1 */
  34#define MCS_PWR_CTRL2   0xC590  /* Power Control Setting 2 for Normal Mode */
  35#define MCS_PWR_CTRL4   0xC5B0  /* Power Control Setting 4 for DC Voltage */
  36#define MCS_PANCTRLSET1 0xCB80  /* Panel Control Setting 1 */
  37#define MCS_PANCTRLSET2 0xCB90  /* Panel Control Setting 2 */
  38#define MCS_PANCTRLSET3 0xCBA0  /* Panel Control Setting 3 */
  39#define MCS_PANCTRLSET4 0xCBB0  /* Panel Control Setting 4 */
  40#define MCS_PANCTRLSET5 0xCBC0  /* Panel Control Setting 5 */
  41#define MCS_PANCTRLSET6 0xCBD0  /* Panel Control Setting 6 */
  42#define MCS_PANCTRLSET7 0xCBE0  /* Panel Control Setting 7 */
  43#define MCS_PANCTRLSET8 0xCBF0  /* Panel Control Setting 8 */
  44#define MCS_PANU2D1     0xCC80  /* Panel U2D Setting 1 */
  45#define MCS_PANU2D2     0xCC90  /* Panel U2D Setting 2 */
  46#define MCS_PANU2D3     0xCCA0  /* Panel U2D Setting 3 */
  47#define MCS_PAND2U1     0xCCB0  /* Panel D2U Setting 1 */
  48#define MCS_PAND2U2     0xCCC0  /* Panel D2U Setting 2 */
  49#define MCS_PAND2U3     0xCCD0  /* Panel D2U Setting 3 */
  50#define MCS_GOAVST      0xCE80  /* GOA VST Setting */
  51#define MCS_GOACLKA1    0xCEA0  /* GOA CLKA1 Setting */
  52#define MCS_GOACLKA3    0xCEB0  /* GOA CLKA3 Setting */
  53#define MCS_GOAECLK     0xCFC0  /* GOA ECLK Setting */
  54#define MCS_NO_DOC2     0xCFD0  /* Command not documented */
  55#define MCS_GVDDSET     0xD800  /* GVDD/NGVDD */
  56#define MCS_VCOMDC      0xD900  /* VCOM Voltage Setting */
  57#define MCS_GMCT2_2P    0xE100  /* Gamma Correction 2.2+ Setting */
  58#define MCS_GMCT2_2N    0xE200  /* Gamma Correction 2.2- Setting */
  59#define MCS_NO_DOC3     0xF5B6  /* Command not documented */
  60#define MCS_CMD2_ENA1   0xFF00  /* Enable Access Command2 "CMD2" */
  61#define MCS_CMD2_ENA2   0xFF80  /* Enable Access Orise Command2 */
  62
  63struct otm8009a {
  64        struct device *dev;
  65        struct drm_panel panel;
  66        struct backlight_device *bl_dev;
  67        struct gpio_desc *reset_gpio;
  68        struct regulator *supply;
  69        bool prepared;
  70        bool enabled;
  71};
  72
  73static const struct drm_display_mode default_mode = {
  74        .clock = 29700,
  75        .hdisplay = 480,
  76        .hsync_start = 480 + 98,
  77        .hsync_end = 480 + 98 + 32,
  78        .htotal = 480 + 98 + 32 + 98,
  79        .vdisplay = 800,
  80        .vsync_start = 800 + 15,
  81        .vsync_end = 800 + 15 + 10,
  82        .vtotal = 800 + 15 + 10 + 14,
  83        .flags = 0,
  84        .width_mm = 52,
  85        .height_mm = 86,
  86};
  87
  88static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
  89{
  90        return container_of(panel, struct otm8009a, panel);
  91}
  92
  93static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
  94                                   size_t len)
  95{
  96        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
  97
  98        if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
  99                dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n");
 100}
 101
 102#define dcs_write_seq(ctx, seq...)                      \
 103({                                                      \
 104        static const u8 d[] = { seq };                  \
 105        otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));  \
 106})
 107
 108#define dcs_write_cmd_at(ctx, cmd, seq...)              \
 109({                                                      \
 110        dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF);   \
 111        dcs_write_seq(ctx, (cmd) >> 8, seq);            \
 112})
 113
 114static int otm8009a_init_sequence(struct otm8009a *ctx)
 115{
 116        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 117        int ret;
 118
 119        /* Enter CMD2 */
 120        dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
 121
 122        /* Enter Orise Command2 */
 123        dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09);
 124
 125        dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30);
 126        mdelay(10);
 127
 128        dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40);
 129        mdelay(10);
 130
 131        dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9);
 132        dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34);
 133        dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50);
 134        dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E);
 135        dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
 136        dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01);
 137        dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34);
 138        dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33);
 139        dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79);
 140        dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B);
 141        dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83);
 142        dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83);
 143        dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E);
 144        dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01);
 145
 146        dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
 147        dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
 148                         0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
 149        dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
 150                         0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
 151        dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
 152                         0x01, 0x02, 0x00, 0x00);
 153
 154        dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00);
 155
 156        dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 157        dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 158                         0, 0, 0, 0, 0);
 159        dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 160                         0, 0, 0, 0, 0);
 161        dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 162        dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
 163                         0, 0, 0, 0, 0);
 164        dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
 165                         4, 0, 0, 0, 0);
 166        dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 167        dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 168                         0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
 169
 170        dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
 171                         0x00, 0x00, 0x00, 0x00);
 172        dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 173                         0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
 174        dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
 175                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 176        dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
 177                         0x00, 0x00, 0x00, 0x00);
 178        dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 179                         0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
 180        dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
 181                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 182
 183        dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66);
 184
 185        dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06);
 186
 187        dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
 188                         0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
 189                         0x01);
 190        dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
 191                         0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
 192                         0x01);
 193
 194        /* Exit CMD2 */
 195        dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
 196
 197        ret = mipi_dsi_dcs_nop(dsi);
 198        if (ret)
 199                return ret;
 200
 201        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 202        if (ret)
 203                return ret;
 204
 205        /* Wait for sleep out exit */
 206        mdelay(120);
 207
 208        /* Default portrait 480x800 rgb24 */
 209        dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
 210
 211        ret = mipi_dsi_dcs_set_column_address(dsi, 0,
 212                                              default_mode.hdisplay - 1);
 213        if (ret)
 214                return ret;
 215
 216        ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
 217        if (ret)
 218                return ret;
 219
 220        /* See otm8009a driver documentation for pixel format descriptions */
 221        ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
 222                                            MIPI_DCS_PIXEL_FMT_24BIT << 4);
 223        if (ret)
 224                return ret;
 225
 226        /* Disable CABC feature */
 227        dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
 228
 229        ret = mipi_dsi_dcs_set_display_on(dsi);
 230        if (ret)
 231                return ret;
 232
 233        ret = mipi_dsi_dcs_nop(dsi);
 234        if (ret)
 235                return ret;
 236
 237        /* Send Command GRAM memory write (no parameters) */
 238        dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START);
 239
 240        /* Wait a short while to let the panel be ready before the 1st frame */
 241        mdelay(10);
 242
 243        return 0;
 244}
 245
 246static int otm8009a_disable(struct drm_panel *panel)
 247{
 248        struct otm8009a *ctx = panel_to_otm8009a(panel);
 249        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 250        int ret;
 251
 252        if (!ctx->enabled)
 253                return 0; /* This is not an issue so we return 0 here */
 254
 255        backlight_disable(ctx->bl_dev);
 256
 257        ret = mipi_dsi_dcs_set_display_off(dsi);
 258        if (ret)
 259                return ret;
 260
 261        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
 262        if (ret)
 263                return ret;
 264
 265        msleep(120);
 266
 267        ctx->enabled = false;
 268
 269        return 0;
 270}
 271
 272static int otm8009a_unprepare(struct drm_panel *panel)
 273{
 274        struct otm8009a *ctx = panel_to_otm8009a(panel);
 275
 276        if (!ctx->prepared)
 277                return 0;
 278
 279        if (ctx->reset_gpio) {
 280                gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 281                msleep(20);
 282        }
 283
 284        regulator_disable(ctx->supply);
 285
 286        ctx->prepared = false;
 287
 288        return 0;
 289}
 290
 291static int otm8009a_prepare(struct drm_panel *panel)
 292{
 293        struct otm8009a *ctx = panel_to_otm8009a(panel);
 294        int ret;
 295
 296        if (ctx->prepared)
 297                return 0;
 298
 299        ret = regulator_enable(ctx->supply);
 300        if (ret < 0) {
 301                dev_err(panel->dev, "failed to enable supply: %d\n", ret);
 302                return ret;
 303        }
 304
 305        if (ctx->reset_gpio) {
 306                gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 307                gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 308                msleep(20);
 309                gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 310                msleep(100);
 311        }
 312
 313        ret = otm8009a_init_sequence(ctx);
 314        if (ret)
 315                return ret;
 316
 317        ctx->prepared = true;
 318
 319        return 0;
 320}
 321
 322static int otm8009a_enable(struct drm_panel *panel)
 323{
 324        struct otm8009a *ctx = panel_to_otm8009a(panel);
 325
 326        if (ctx->enabled)
 327                return 0;
 328
 329        backlight_enable(ctx->bl_dev);
 330
 331        ctx->enabled = true;
 332
 333        return 0;
 334}
 335
 336static int otm8009a_get_modes(struct drm_panel *panel,
 337                              struct drm_connector *connector)
 338{
 339        struct drm_display_mode *mode;
 340
 341        mode = drm_mode_duplicate(connector->dev, &default_mode);
 342        if (!mode) {
 343                dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
 344                        default_mode.hdisplay, default_mode.vdisplay,
 345                        drm_mode_vrefresh(&default_mode));
 346                return -ENOMEM;
 347        }
 348
 349        drm_mode_set_name(mode);
 350
 351        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 352        drm_mode_probed_add(connector, mode);
 353
 354        connector->display_info.width_mm = mode->width_mm;
 355        connector->display_info.height_mm = mode->height_mm;
 356
 357        return 1;
 358}
 359
 360static const struct drm_panel_funcs otm8009a_drm_funcs = {
 361        .disable   = otm8009a_disable,
 362        .unprepare = otm8009a_unprepare,
 363        .prepare   = otm8009a_prepare,
 364        .enable    = otm8009a_enable,
 365        .get_modes = otm8009a_get_modes,
 366};
 367
 368/*
 369 * DSI-BASED BACKLIGHT
 370 */
 371
 372static int otm8009a_backlight_update_status(struct backlight_device *bd)
 373{
 374        struct otm8009a *ctx = bl_get_data(bd);
 375        u8 data[2];
 376
 377        if (!ctx->prepared) {
 378                dev_dbg(&bd->dev, "lcd not ready yet for setting its backlight!\n");
 379                return -ENXIO;
 380        }
 381
 382        if (bd->props.power <= FB_BLANK_NORMAL) {
 383                /* Power on the backlight with the requested brightness
 384                 * Note We can not use mipi_dsi_dcs_set_display_brightness()
 385                 * as otm8009a driver support only 8-bit brightness (1 param).
 386                 */
 387                data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
 388                data[1] = bd->props.brightness;
 389                otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
 390
 391                /* set Brightness Control & Backlight on */
 392                data[1] = 0x24;
 393
 394        } else {
 395                /* Power off the backlight: set Brightness Control & Bl off */
 396                data[1] = 0;
 397        }
 398
 399        /* Update Brightness Control & Backlight */
 400        data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
 401        otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
 402
 403        return 0;
 404}
 405
 406static const struct backlight_ops otm8009a_backlight_ops = {
 407        .update_status = otm8009a_backlight_update_status,
 408};
 409
 410static int otm8009a_probe(struct mipi_dsi_device *dsi)
 411{
 412        struct device *dev = &dsi->dev;
 413        struct otm8009a *ctx;
 414        int ret;
 415
 416        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 417        if (!ctx)
 418                return -ENOMEM;
 419
 420        ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
 421        if (IS_ERR(ctx->reset_gpio)) {
 422                dev_err(dev, "cannot get reset-gpio\n");
 423                return PTR_ERR(ctx->reset_gpio);
 424        }
 425
 426        ctx->supply = devm_regulator_get(dev, "power");
 427        if (IS_ERR(ctx->supply)) {
 428                ret = PTR_ERR(ctx->supply);
 429                if (ret != -EPROBE_DEFER)
 430                        dev_err(dev, "failed to request regulator: %d\n", ret);
 431                return ret;
 432        }
 433
 434        mipi_dsi_set_drvdata(dsi, ctx);
 435
 436        ctx->dev = dev;
 437
 438        dsi->lanes = 2;
 439        dsi->format = MIPI_DSI_FMT_RGB888;
 440        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 441                          MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS;
 442
 443        drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs,
 444                       DRM_MODE_CONNECTOR_DSI);
 445
 446        ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev),
 447                                                     dsi->host->dev, ctx,
 448                                                     &otm8009a_backlight_ops,
 449                                                     NULL);
 450        if (IS_ERR(ctx->bl_dev)) {
 451                ret = PTR_ERR(ctx->bl_dev);
 452                dev_err(dev, "failed to register backlight: %d\n", ret);
 453                return ret;
 454        }
 455
 456        ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
 457        ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
 458        ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
 459        ctx->bl_dev->props.type = BACKLIGHT_RAW;
 460
 461        drm_panel_add(&ctx->panel);
 462
 463        ret = mipi_dsi_attach(dsi);
 464        if (ret < 0) {
 465                dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
 466                drm_panel_remove(&ctx->panel);
 467                return ret;
 468        }
 469
 470        return 0;
 471}
 472
 473static int otm8009a_remove(struct mipi_dsi_device *dsi)
 474{
 475        struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
 476
 477        mipi_dsi_detach(dsi);
 478        drm_panel_remove(&ctx->panel);
 479
 480        return 0;
 481}
 482
 483static const struct of_device_id orisetech_otm8009a_of_match[] = {
 484        { .compatible = "orisetech,otm8009a" },
 485        { }
 486};
 487MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
 488
 489static struct mipi_dsi_driver orisetech_otm8009a_driver = {
 490        .probe  = otm8009a_probe,
 491        .remove = otm8009a_remove,
 492        .driver = {
 493                .name = "panel-orisetech-otm8009a",
 494                .of_match_table = orisetech_otm8009a_of_match,
 495        },
 496};
 497module_mipi_dsi_driver(orisetech_otm8009a_driver);
 498
 499MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
 500MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
 501MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
 502MODULE_LICENSE("GPL v2");
 503