uboot/drivers/video/orisetech_otm8009a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2019 STMicroelectronics - All Rights Reserved
   4 * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
   5 *            Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
   6 *
   7 * This otm8009a panel driver is inspired from the Linux Kernel driver
   8 * drivers/gpu/drm/panel/panel-orisetech-otm8009a.c.
   9 */
  10#include <common.h>
  11#include <backlight.h>
  12#include <dm.h>
  13#include <mipi_dsi.h>
  14#include <panel.h>
  15#include <asm/gpio.h>
  16#include <dm/device_compat.h>
  17#include <linux/delay.h>
  18#include <power/regulator.h>
  19
  20#define OTM8009A_BACKLIGHT_DEFAULT      240
  21#define OTM8009A_BACKLIGHT_MAX          255
  22
  23/* Manufacturer Command Set */
  24#define MCS_ADRSFT      0x0000  /* Address Shift Function */
  25#define MCS_PANSET      0xB3A6  /* Panel Type Setting */
  26#define MCS_SD_CTRL     0xC0A2  /* Source Driver Timing Setting */
  27#define MCS_P_DRV_M     0xC0B4  /* Panel Driving Mode */
  28#define MCS_OSC_ADJ     0xC181  /* Oscillator Adjustment for Idle/Normal mode */
  29#define MCS_RGB_VID_SET 0xC1A1  /* RGB Video Mode Setting */
  30#define MCS_SD_PCH_CTRL 0xC480  /* Source Driver Precharge Control */
  31#define MCS_NO_DOC1     0xC48A  /* Command not documented */
  32#define MCS_PWR_CTRL1   0xC580  /* Power Control Setting 1 */
  33#define MCS_PWR_CTRL2   0xC590  /* Power Control Setting 2 for Normal Mode */
  34#define MCS_PWR_CTRL4   0xC5B0  /* Power Control Setting 4 for DC Voltage */
  35#define MCS_PANCTRLSET1 0xCB80  /* Panel Control Setting 1 */
  36#define MCS_PANCTRLSET2 0xCB90  /* Panel Control Setting 2 */
  37#define MCS_PANCTRLSET3 0xCBA0  /* Panel Control Setting 3 */
  38#define MCS_PANCTRLSET4 0xCBB0  /* Panel Control Setting 4 */
  39#define MCS_PANCTRLSET5 0xCBC0  /* Panel Control Setting 5 */
  40#define MCS_PANCTRLSET6 0xCBD0  /* Panel Control Setting 6 */
  41#define MCS_PANCTRLSET7 0xCBE0  /* Panel Control Setting 7 */
  42#define MCS_PANCTRLSET8 0xCBF0  /* Panel Control Setting 8 */
  43#define MCS_PANU2D1     0xCC80  /* Panel U2D Setting 1 */
  44#define MCS_PANU2D2     0xCC90  /* Panel U2D Setting 2 */
  45#define MCS_PANU2D3     0xCCA0  /* Panel U2D Setting 3 */
  46#define MCS_PAND2U1     0xCCB0  /* Panel D2U Setting 1 */
  47#define MCS_PAND2U2     0xCCC0  /* Panel D2U Setting 2 */
  48#define MCS_PAND2U3     0xCCD0  /* Panel D2U Setting 3 */
  49#define MCS_GOAVST      0xCE80  /* GOA VST Setting */
  50#define MCS_GOACLKA1    0xCEA0  /* GOA CLKA1 Setting */
  51#define MCS_GOACLKA3    0xCEB0  /* GOA CLKA3 Setting */
  52#define MCS_GOAECLK     0xCFC0  /* GOA ECLK Setting */
  53#define MCS_NO_DOC2     0xCFD0  /* Command not documented */
  54#define MCS_GVDDSET     0xD800  /* GVDD/NGVDD */
  55#define MCS_VCOMDC      0xD900  /* VCOM Voltage Setting */
  56#define MCS_GMCT2_2P    0xE100  /* Gamma Correction 2.2+ Setting */
  57#define MCS_GMCT2_2N    0xE200  /* Gamma Correction 2.2- Setting */
  58#define MCS_NO_DOC3     0xF5B6  /* Command not documented */
  59#define MCS_CMD2_ENA1   0xFF00  /* Enable Access Command2 "CMD2" */
  60#define MCS_CMD2_ENA2   0xFF80  /* Enable Access Orise Command2 */
  61
  62struct otm8009a_panel_priv {
  63        struct udevice *reg;
  64        struct gpio_desc reset;
  65};
  66
  67static const struct display_timing default_timing = {
  68        .pixelclock.typ         = 29700000,
  69        .hactive.typ            = 480,
  70        .hfront_porch.typ       = 98,
  71        .hback_porch.typ        = 98,
  72        .hsync_len.typ          = 32,
  73        .vactive.typ            = 800,
  74        .vfront_porch.typ       = 15,
  75        .vback_porch.typ        = 14,
  76        .vsync_len.typ          = 10,
  77};
  78
  79static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data,
  80                                   size_t len)
  81{
  82        struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
  83        struct mipi_dsi_device *device = plat->device;
  84
  85        if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
  86                dev_err(dev, "mipi dsi dcs write buffer failed\n");
  87}
  88
  89static void otm8009a_dcs_write_buf_hs(struct udevice *dev, const void *data,
  90                                      size_t len)
  91{
  92        struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
  93        struct mipi_dsi_device *device = plat->device;
  94
  95        /* data will be sent in dsi hs mode (ie. no lpm) */
  96        device->mode_flags &= ~MIPI_DSI_MODE_LPM;
  97
  98        if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
  99                dev_err(dev, "mipi dsi dcs write buffer failed\n");
 100
 101        /* restore back the dsi lpm mode */
 102        device->mode_flags |= MIPI_DSI_MODE_LPM;
 103}
 104
 105#define dcs_write_seq(dev, seq...)                              \
 106({                                                              \
 107        static const u8 d[] = { seq };                          \
 108        otm8009a_dcs_write_buf(dev, d, ARRAY_SIZE(d));          \
 109})
 110
 111#define dcs_write_seq_hs(dev, seq...)                           \
 112({                                                              \
 113        static const u8 d[] = { seq };                          \
 114        otm8009a_dcs_write_buf_hs(dev, d, ARRAY_SIZE(d));       \
 115})
 116
 117#define dcs_write_cmd_at(dev, cmd, seq...)              \
 118({                                                      \
 119        static const u16 c = cmd;                       \
 120        struct udevice *device = dev;                   \
 121        dcs_write_seq(device, MCS_ADRSFT, (c) & 0xFF);  \
 122        dcs_write_seq(device, (c) >> 8, seq);           \
 123})
 124
 125static int otm8009a_init_sequence(struct udevice *dev)
 126{
 127        struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
 128        struct mipi_dsi_device *device = plat->device;
 129        int ret;
 130
 131        /* Enter CMD2 */
 132        dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
 133
 134        /* Enter Orise Command2 */
 135        dcs_write_cmd_at(dev, MCS_CMD2_ENA2, 0x80, 0x09);
 136
 137        dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL, 0x30);
 138        mdelay(10);
 139
 140        dcs_write_cmd_at(dev, MCS_NO_DOC1, 0x40);
 141        mdelay(10);
 142
 143        dcs_write_cmd_at(dev, MCS_PWR_CTRL4 + 1, 0xA9);
 144        dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 1, 0x34);
 145        dcs_write_cmd_at(dev, MCS_P_DRV_M, 0x50);
 146        dcs_write_cmd_at(dev, MCS_VCOMDC, 0x4E);
 147        dcs_write_cmd_at(dev, MCS_OSC_ADJ, 0x66); /* 65Hz */
 148        dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 2, 0x01);
 149        dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 5, 0x34);
 150        dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 4, 0x33);
 151        dcs_write_cmd_at(dev, MCS_GVDDSET, 0x79, 0x79);
 152        dcs_write_cmd_at(dev, MCS_SD_CTRL + 1, 0x1B);
 153        dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 2, 0x83);
 154        dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL + 1, 0x83);
 155        dcs_write_cmd_at(dev, MCS_RGB_VID_SET, 0x0E);
 156        dcs_write_cmd_at(dev, MCS_PANSET, 0x00, 0x01);
 157
 158        dcs_write_cmd_at(dev, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
 159        dcs_write_cmd_at(dev, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
 160                         0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
 161        dcs_write_cmd_at(dev, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
 162                         0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
 163        dcs_write_cmd_at(dev, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
 164                         0x01, 0x02, 0x00, 0x00);
 165
 166        dcs_write_cmd_at(dev, MCS_NO_DOC2, 0x00);
 167
 168        dcs_write_cmd_at(dev, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 169        dcs_write_cmd_at(dev, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 170                         0, 0, 0, 0, 0);
 171        dcs_write_cmd_at(dev, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 172                         0, 0, 0, 0, 0);
 173        dcs_write_cmd_at(dev, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 174        dcs_write_cmd_at(dev, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
 175                         0, 0, 0, 0, 0);
 176        dcs_write_cmd_at(dev, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
 177                         4, 0, 0, 0, 0);
 178        dcs_write_cmd_at(dev, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 179        dcs_write_cmd_at(dev, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 180                         0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
 181
 182        dcs_write_cmd_at(dev, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
 183                         0x00, 0x00, 0x00, 0x00);
 184        dcs_write_cmd_at(dev, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 185                         0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
 186        dcs_write_cmd_at(dev, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
 187                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 188        dcs_write_cmd_at(dev, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
 189                         0x00, 0x00, 0x00, 0x00);
 190        dcs_write_cmd_at(dev, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 191                         0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
 192        dcs_write_cmd_at(dev, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
 193                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 194
 195        dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 1, 0x66);
 196
 197        dcs_write_cmd_at(dev, MCS_NO_DOC3, 0x06);
 198
 199        dcs_write_cmd_at(dev, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
 200                         0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
 201                         0x01);
 202        dcs_write_cmd_at(dev, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
 203                         0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
 204                         0x01);
 205
 206        /* Exit CMD2 */
 207        dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
 208
 209        ret =  mipi_dsi_dcs_nop(device);
 210        if (ret)
 211                return ret;
 212
 213        ret = mipi_dsi_dcs_exit_sleep_mode(device);
 214        if (ret)
 215                return ret;
 216
 217        /* Wait for sleep out exit */
 218        mdelay(120);
 219
 220        /* Default portrait 480x800 rgb24 */
 221        dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
 222
 223        ret =  mipi_dsi_dcs_set_column_address(device, 0,
 224                                               default_timing.hactive.typ - 1);
 225        if (ret)
 226                return ret;
 227
 228        ret =  mipi_dsi_dcs_set_page_address(device, 0,
 229                                             default_timing.vactive.typ - 1);
 230        if (ret)
 231                return ret;
 232
 233        /* See otm8009a driver documentation for pixel format descriptions */
 234        ret =  mipi_dsi_dcs_set_pixel_format(device, MIPI_DCS_PIXEL_FMT_24BIT |
 235                                             MIPI_DCS_PIXEL_FMT_24BIT << 4);
 236        if (ret)
 237                return ret;
 238
 239        /* Disable CABC feature */
 240        dcs_write_seq(dev, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
 241
 242        ret = mipi_dsi_dcs_set_display_on(device);
 243        if (ret)
 244                return ret;
 245
 246        ret = mipi_dsi_dcs_nop(device);
 247        if (ret)
 248                return ret;
 249
 250        /* Send Command GRAM memory write (no parameters) */
 251        dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START);
 252
 253        return 0;
 254}
 255
 256static int otm8009a_panel_enable_backlight(struct udevice *dev)
 257{
 258        struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
 259        struct mipi_dsi_device *device = plat->device;
 260        int ret;
 261
 262        ret = mipi_dsi_attach(device);
 263        if (ret < 0)
 264                return ret;
 265
 266        ret = otm8009a_init_sequence(dev);
 267        if (ret)
 268                return ret;
 269
 270        /*
 271         * Power on the backlight with the requested brightness
 272         * Note We can not use mipi_dsi_dcs_set_display_brightness()
 273         * as otm8009a driver support only 8-bit brightness (1 param).
 274         */
 275        dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
 276                      OTM8009A_BACKLIGHT_DEFAULT);
 277
 278        /* Update Brightness Control & Backlight */
 279        dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
 280
 281        /* Update Brightness Control & Backlight */
 282        dcs_write_seq_hs(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY);
 283
 284        /* Need to wait a few time before sending the first image */
 285        mdelay(10);
 286
 287        return 0;
 288}
 289
 290static int otm8009a_panel_get_display_timing(struct udevice *dev,
 291                                             struct display_timing *timings)
 292{
 293        memcpy(timings, &default_timing, sizeof(*timings));
 294
 295        return 0;
 296}
 297
 298static int otm8009a_panel_of_to_plat(struct udevice *dev)
 299{
 300        struct otm8009a_panel_priv *priv = dev_get_priv(dev);
 301        int ret;
 302
 303        if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
 304                ret =  device_get_supply_regulator(dev, "power-supply",
 305                                                   &priv->reg);
 306                if (ret && ret != -ENOENT) {
 307                        dev_err(dev, "Warning: cannot get power supply\n");
 308                        return ret;
 309                }
 310        }
 311
 312        ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
 313                                   GPIOD_IS_OUT);
 314        if (ret) {
 315                dev_err(dev, "warning: cannot get reset GPIO\n");
 316                if (ret != -ENOENT)
 317                        return ret;
 318        }
 319
 320        return 0;
 321}
 322
 323static int otm8009a_panel_probe(struct udevice *dev)
 324{
 325        struct otm8009a_panel_priv *priv = dev_get_priv(dev);
 326        struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
 327        int ret;
 328
 329        if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
 330                dev_dbg(dev, "enable regulator '%s'\n", priv->reg->name);
 331                ret = regulator_set_enable(priv->reg, true);
 332                if (ret)
 333                        return ret;
 334        }
 335
 336        /* reset panel */
 337        dm_gpio_set_value(&priv->reset, true);
 338        mdelay(1); /* >50us */
 339        dm_gpio_set_value(&priv->reset, false);
 340        mdelay(10); /* >5ms */
 341
 342        /* fill characteristics of DSI data link */
 343        plat->lanes = 2;
 344        plat->format = MIPI_DSI_FMT_RGB888;
 345        plat->mode_flags = MIPI_DSI_MODE_VIDEO |
 346                           MIPI_DSI_MODE_VIDEO_BURST |
 347                           MIPI_DSI_MODE_LPM;
 348
 349        return 0;
 350}
 351
 352static const struct panel_ops otm8009a_panel_ops = {
 353        .enable_backlight = otm8009a_panel_enable_backlight,
 354        .get_display_timing = otm8009a_panel_get_display_timing,
 355};
 356
 357static const struct udevice_id otm8009a_panel_ids[] = {
 358        { .compatible = "orisetech,otm8009a" },
 359        { }
 360};
 361
 362U_BOOT_DRIVER(otm8009a_panel) = {
 363        .name                     = "otm8009a_panel",
 364        .id                       = UCLASS_PANEL,
 365        .of_match                 = otm8009a_panel_ids,
 366        .ops                      = &otm8009a_panel_ops,
 367        .of_to_plat       = otm8009a_panel_of_to_plat,
 368        .probe                    = otm8009a_panel_probe,
 369        .plat_auto      = sizeof(struct mipi_dsi_panel_plat),
 370        .priv_auto      = sizeof(struct otm8009a_panel_priv),
 371};
 372