linux/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
   4 * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
   5 *
   6 * based on
   7 *
   8 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
   9 * Copyright (C) Purism SPC 2019
  10 */
  11
  12#include <drm/drm_mipi_dsi.h>
  13#include <drm/drm_modes.h>
  14#include <drm/drm_panel.h>
  15
  16#include <video/display_timing.h>
  17#include <video/mipi_display.h>
  18
  19#include <linux/delay.h>
  20#include <linux/gpio/consumer.h>
  21#include <linux/media-bus-format.h>
  22#include <linux/module.h>
  23#include <linux/of.h>
  24#include <linux/regulator/consumer.h>
  25
  26/* Manufacturer specific Commands send via DSI */
  27#define XPP055C272_CMD_ALL_PIXEL_OFF    0x22
  28#define XPP055C272_CMD_ALL_PIXEL_ON     0x23
  29#define XPP055C272_CMD_SETDISP          0xb2
  30#define XPP055C272_CMD_SETRGBIF         0xb3
  31#define XPP055C272_CMD_SETCYC           0xb4
  32#define XPP055C272_CMD_SETBGP           0xb5
  33#define XPP055C272_CMD_SETVCOM          0xb6
  34#define XPP055C272_CMD_SETOTP           0xb7
  35#define XPP055C272_CMD_SETPOWER_EXT     0xb8
  36#define XPP055C272_CMD_SETEXTC          0xb9
  37#define XPP055C272_CMD_SETMIPI          0xbA
  38#define XPP055C272_CMD_SETVDC           0xbc
  39#define XPP055C272_CMD_SETPCR           0xbf
  40#define XPP055C272_CMD_SETSCR           0xc0
  41#define XPP055C272_CMD_SETPOWER         0xc1
  42#define XPP055C272_CMD_SETECO           0xc6
  43#define XPP055C272_CMD_SETPANEL         0xcc
  44#define XPP055C272_CMD_SETGAMMA         0xe0
  45#define XPP055C272_CMD_SETEQ            0xe3
  46#define XPP055C272_CMD_SETGIP1          0xe9
  47#define XPP055C272_CMD_SETGIP2          0xea
  48
  49struct xpp055c272 {
  50        struct device *dev;
  51        struct drm_panel panel;
  52        struct gpio_desc *reset_gpio;
  53        struct regulator *vci;
  54        struct regulator *iovcc;
  55        bool prepared;
  56};
  57
  58static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
  59{
  60        return container_of(panel, struct xpp055c272, panel);
  61}
  62
  63#define dsi_generic_write_seq(dsi, cmd, seq...) do {                    \
  64                static const u8 b[] = { cmd, seq };                     \
  65                int ret;                                                \
  66                ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
  67                if (ret < 0)                                            \
  68                        return ret;                                     \
  69        } while (0)
  70
  71static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
  72{
  73        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
  74        struct device *dev = ctx->dev;
  75
  76        /*
  77         * Init sequence was supplied by the panel vendor without much
  78         * documentation.
  79         */
  80        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
  81        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI,
  82                              0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
  83                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
  84                              0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
  85                              0x00, 0x00, 0x37);
  86        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);
  87        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
  88        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF,
  89                              0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
  90                              0x00, 0x00);
  91        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR,
  92                              0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
  93                              0x00);
  94        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);
  95        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);
  96        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);
  97        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
  98        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ,
  99                              0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
 100                              0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
 101        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER,
 102                              0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
 103                              0x67, 0x77, 0x33, 0x33);
 104        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
 105                              0xff, 0x01, 0xff);
 106        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
 107        msleep(20);
 108
 109        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
 110        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1,
 111                              0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
 112                              0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
 113                              0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
 114                              0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
 115                              0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
 116                              0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
 117                              0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
 118                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 119        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2,
 120                              0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
 121                              0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
 122                              0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
 123                              0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
 124                              0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
 125                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 126                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
 127                              0xa0, 0x00, 0x00, 0x00, 0x00);
 128        dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA,
 129                              0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
 130                              0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
 131                              0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
 132                              0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
 133                              0x11, 0x18);
 134
 135        msleep(60);
 136
 137        dev_dbg(dev, "Panel init sequence done\n");
 138        return 0;
 139}
 140
 141static int xpp055c272_unprepare(struct drm_panel *panel)
 142{
 143        struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
 144        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 145        int ret;
 146
 147        if (!ctx->prepared)
 148                return 0;
 149
 150        ret = mipi_dsi_dcs_set_display_off(dsi);
 151        if (ret < 0)
 152                dev_err(ctx->dev, "failed to set display off: %d\n", ret);
 153
 154        mipi_dsi_dcs_enter_sleep_mode(dsi);
 155        if (ret < 0) {
 156                dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
 157                return ret;
 158        }
 159
 160        regulator_disable(ctx->iovcc);
 161        regulator_disable(ctx->vci);
 162
 163        ctx->prepared = false;
 164
 165        return 0;
 166}
 167
 168static int xpp055c272_prepare(struct drm_panel *panel)
 169{
 170        struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
 171        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 172        int ret;
 173
 174        if (ctx->prepared)
 175                return 0;
 176
 177        dev_dbg(ctx->dev, "Resetting the panel\n");
 178        ret = regulator_enable(ctx->vci);
 179        if (ret < 0) {
 180                dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
 181                return ret;
 182        }
 183        ret = regulator_enable(ctx->iovcc);
 184        if (ret < 0) {
 185                dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
 186                goto disable_vci;
 187        }
 188
 189        gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 190        /* T6: 10us */
 191        usleep_range(10, 20);
 192        gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 193
 194        /* T8: 20ms */
 195        msleep(20);
 196
 197        ret = xpp055c272_init_sequence(ctx);
 198        if (ret < 0) {
 199                dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
 200                goto disable_iovcc;
 201        }
 202
 203        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 204        if (ret < 0) {
 205                dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
 206                goto disable_iovcc;
 207        }
 208
 209        /* T9: 120ms */
 210        msleep(120);
 211
 212        ret = mipi_dsi_dcs_set_display_on(dsi);
 213        if (ret < 0) {
 214                dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
 215                goto disable_iovcc;
 216        }
 217
 218        msleep(50);
 219
 220        ctx->prepared = true;
 221
 222        return 0;
 223
 224disable_iovcc:
 225        regulator_disable(ctx->iovcc);
 226disable_vci:
 227        regulator_disable(ctx->vci);
 228        return ret;
 229}
 230
 231static const struct drm_display_mode default_mode = {
 232        .hdisplay       = 720,
 233        .hsync_start    = 720 + 40,
 234        .hsync_end      = 720 + 40 + 10,
 235        .htotal         = 720 + 40 + 10 + 40,
 236        .vdisplay       = 1280,
 237        .vsync_start    = 1280 + 22,
 238        .vsync_end      = 1280 + 22 + 4,
 239        .vtotal         = 1280 + 22 + 4 + 11,
 240        .clock          = 64000,
 241        .width_mm       = 68,
 242        .height_mm      = 121,
 243};
 244
 245static int xpp055c272_get_modes(struct drm_panel *panel,
 246                                struct drm_connector *connector)
 247{
 248        struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
 249        struct drm_display_mode *mode;
 250
 251        mode = drm_mode_duplicate(connector->dev, &default_mode);
 252        if (!mode) {
 253                dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
 254                        default_mode.hdisplay, default_mode.vdisplay,
 255                        drm_mode_vrefresh(&default_mode));
 256                return -ENOMEM;
 257        }
 258
 259        drm_mode_set_name(mode);
 260
 261        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 262        connector->display_info.width_mm = mode->width_mm;
 263        connector->display_info.height_mm = mode->height_mm;
 264        drm_mode_probed_add(connector, mode);
 265
 266        return 1;
 267}
 268
 269static const struct drm_panel_funcs xpp055c272_funcs = {
 270        .unprepare      = xpp055c272_unprepare,
 271        .prepare        = xpp055c272_prepare,
 272        .get_modes      = xpp055c272_get_modes,
 273};
 274
 275static int xpp055c272_probe(struct mipi_dsi_device *dsi)
 276{
 277        struct device *dev = &dsi->dev;
 278        struct xpp055c272 *ctx;
 279        int ret;
 280
 281        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 282        if (!ctx)
 283                return -ENOMEM;
 284
 285        ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
 286        if (IS_ERR(ctx->reset_gpio)) {
 287                dev_err(dev, "cannot get reset gpio\n");
 288                return PTR_ERR(ctx->reset_gpio);
 289        }
 290
 291        ctx->vci = devm_regulator_get(dev, "vci");
 292        if (IS_ERR(ctx->vci)) {
 293                ret = PTR_ERR(ctx->vci);
 294                if (ret != -EPROBE_DEFER)
 295                        dev_err(dev, "Failed to request vci regulator: %d\n", ret);
 296                return ret;
 297        }
 298
 299        ctx->iovcc = devm_regulator_get(dev, "iovcc");
 300        if (IS_ERR(ctx->iovcc)) {
 301                ret = PTR_ERR(ctx->iovcc);
 302                if (ret != -EPROBE_DEFER)
 303                        dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
 304                return ret;
 305        }
 306
 307        mipi_dsi_set_drvdata(dsi, ctx);
 308
 309        ctx->dev = dev;
 310
 311        dsi->lanes = 4;
 312        dsi->format = MIPI_DSI_FMT_RGB888;
 313        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 314                          MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
 315
 316        drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
 317                       DRM_MODE_CONNECTOR_DSI);
 318
 319        ret = drm_panel_of_backlight(&ctx->panel);
 320        if (ret)
 321                return ret;
 322
 323        drm_panel_add(&ctx->panel);
 324
 325        ret = mipi_dsi_attach(dsi);
 326        if (ret < 0) {
 327                dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
 328                drm_panel_remove(&ctx->panel);
 329                return ret;
 330        }
 331
 332        return 0;
 333}
 334
 335static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
 336{
 337        struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
 338        int ret;
 339
 340        ret = drm_panel_unprepare(&ctx->panel);
 341        if (ret < 0)
 342                dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
 343
 344        ret = drm_panel_disable(&ctx->panel);
 345        if (ret < 0)
 346                dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
 347}
 348
 349static int xpp055c272_remove(struct mipi_dsi_device *dsi)
 350{
 351        struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
 352        int ret;
 353
 354        xpp055c272_shutdown(dsi);
 355
 356        ret = mipi_dsi_detach(dsi);
 357        if (ret < 0)
 358                dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
 359
 360        drm_panel_remove(&ctx->panel);
 361
 362        return 0;
 363}
 364
 365static const struct of_device_id xpp055c272_of_match[] = {
 366        { .compatible = "xinpeng,xpp055c272" },
 367        { /* sentinel */ }
 368};
 369MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
 370
 371static struct mipi_dsi_driver xpp055c272_driver = {
 372        .driver = {
 373                .name = "panel-xinpeng-xpp055c272",
 374                .of_match_table = xpp055c272_of_match,
 375        },
 376        .probe  = xpp055c272_probe,
 377        .remove = xpp055c272_remove,
 378        .shutdown = xpp055c272_shutdown,
 379};
 380module_mipi_dsi_driver(xpp055c272_driver);
 381
 382MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
 383MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
 384MODULE_LICENSE("GPL v2");
 385