linux/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
   4 *
   5 * Copyright (C) Purism SPC 2019
   6 */
   7
   8#include <drm/drm_mipi_dsi.h>
   9#include <drm/drm_modes.h>
  10#include <drm/drm_panel.h>
  11#include <drm/drm_print.h>
  12#include <linux/backlight.h>
  13#include <linux/debugfs.h>
  14#include <linux/delay.h>
  15#include <linux/gpio/consumer.h>
  16#include <linux/media-bus-format.h>
  17#include <linux/module.h>
  18#include <video/display_timing.h>
  19#include <video/mipi_display.h>
  20
  21#define DRV_NAME "panel-rocktech-jh057n00900"
  22
  23/* Manufacturer specific Commands send via DSI */
  24#define ST7703_CMD_ALL_PIXEL_OFF 0x22
  25#define ST7703_CMD_ALL_PIXEL_ON  0x23
  26#define ST7703_CMD_SETDISP       0xB2
  27#define ST7703_CMD_SETRGBIF      0xB3
  28#define ST7703_CMD_SETCYC        0xB4
  29#define ST7703_CMD_SETBGP        0xB5
  30#define ST7703_CMD_SETVCOM       0xB6
  31#define ST7703_CMD_SETOTP        0xB7
  32#define ST7703_CMD_SETPOWER_EXT  0xB8
  33#define ST7703_CMD_SETEXTC       0xB9
  34#define ST7703_CMD_SETMIPI       0xBA
  35#define ST7703_CMD_SETVDC        0xBC
  36#define ST7703_CMD_SETSCR        0xC0
  37#define ST7703_CMD_SETPOWER      0xC1
  38#define ST7703_CMD_SETPANEL      0xCC
  39#define ST7703_CMD_SETGAMMA      0xE0
  40#define ST7703_CMD_SETEQ         0xE3
  41#define ST7703_CMD_SETGIP1       0xE9
  42#define ST7703_CMD_SETGIP2       0xEA
  43
  44struct jh057n {
  45        struct device *dev;
  46        struct drm_panel panel;
  47        struct gpio_desc *reset_gpio;
  48        struct backlight_device *backlight;
  49        bool prepared;
  50
  51        struct dentry *debugfs;
  52};
  53
  54static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel)
  55{
  56        return container_of(panel, struct jh057n, panel);
  57}
  58
  59#define dsi_generic_write_seq(dsi, seq...) do {                         \
  60                static const u8 d[] = { seq };                          \
  61                int ret;                                                \
  62                ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d));    \
  63                if (ret < 0)                                            \
  64                        return ret;                                     \
  65        } while (0)
  66
  67static int jh057n_init_sequence(struct jh057n *ctx)
  68{
  69        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
  70        struct device *dev = ctx->dev;
  71        int ret;
  72
  73        /*
  74         * Init sequence was supplied by the panel vendor. Most of the commands
  75         * resemble the ST7703 but the number of parameters often don't match
  76         * so it's likely a clone.
  77         */
  78        dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
  79                              0xF1, 0x12, 0x83);
  80        dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
  81                              0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
  82                              0x00, 0x00);
  83        dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
  84                              0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
  85                              0x00);
  86        dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
  87        dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
  88        dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
  89        dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
  90        dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ,
  91                              0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
  92                              0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
  93        dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08);
  94        msleep(20);
  95
  96        dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
  97        dsi_generic_write_seq(dsi, 0xBF, 0x02, 0x11, 0x00);
  98        dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1,
  99                              0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
 100                              0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
 101                              0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
 102                              0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
 103                              0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
 104                              0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
 105                              0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 106                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
 107        dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2,
 108                              0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 109                              0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
 110                              0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
 111                              0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
 112                              0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
 113                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 114                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
 115                              0xA5, 0x00, 0x00, 0x00, 0x00);
 116        dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA,
 117                              0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
 118                              0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
 119                              0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
 120                              0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
 121                              0x11, 0x18);
 122        msleep(20);
 123
 124        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 125        if (ret < 0) {
 126                DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
 127                return ret;
 128        }
 129        /* Panel is operational 120 msec after reset */
 130        msleep(60);
 131        ret = mipi_dsi_dcs_set_display_on(dsi);
 132        if (ret)
 133                return ret;
 134
 135        DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
 136        return 0;
 137}
 138
 139static int jh057n_enable(struct drm_panel *panel)
 140{
 141        struct jh057n *ctx = panel_to_jh057n(panel);
 142
 143        return backlight_enable(ctx->backlight);
 144}
 145
 146static int jh057n_disable(struct drm_panel *panel)
 147{
 148        struct jh057n *ctx = panel_to_jh057n(panel);
 149
 150        return backlight_disable(ctx->backlight);
 151}
 152
 153static int jh057n_unprepare(struct drm_panel *panel)
 154{
 155        struct jh057n *ctx = panel_to_jh057n(panel);
 156        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 157
 158        if (!ctx->prepared)
 159                return 0;
 160
 161        mipi_dsi_dcs_set_display_off(dsi);
 162        ctx->prepared = false;
 163
 164        return 0;
 165}
 166
 167static int jh057n_prepare(struct drm_panel *panel)
 168{
 169        struct jh057n *ctx = panel_to_jh057n(panel);
 170        int ret;
 171
 172        if (ctx->prepared)
 173                return 0;
 174
 175        DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
 176        gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 177        usleep_range(20, 40);
 178        gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 179        msleep(20);
 180
 181        ret = jh057n_init_sequence(ctx);
 182        if (ret < 0) {
 183                DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
 184                              ret);
 185                return ret;
 186        }
 187
 188        ctx->prepared = true;
 189
 190        return 0;
 191}
 192
 193static const struct drm_display_mode default_mode = {
 194        .hdisplay    = 720,
 195        .hsync_start = 720 + 90,
 196        .hsync_end   = 720 + 90 + 20,
 197        .htotal      = 720 + 90 + 20 + 20,
 198        .vdisplay    = 1440,
 199        .vsync_start = 1440 + 20,
 200        .vsync_end   = 1440 + 20 + 4,
 201        .vtotal      = 1440 + 20 + 4 + 12,
 202        .vrefresh    = 60,
 203        .clock       = 75276,
 204        .flags       = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
 205        .width_mm    = 65,
 206        .height_mm   = 130,
 207};
 208
 209static int jh057n_get_modes(struct drm_panel *panel)
 210{
 211        struct jh057n *ctx = panel_to_jh057n(panel);
 212        struct drm_display_mode *mode;
 213
 214        mode = drm_mode_duplicate(panel->drm, &default_mode);
 215        if (!mode) {
 216                DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n",
 217                              default_mode.hdisplay, default_mode.vdisplay,
 218                              default_mode.vrefresh);
 219                return -ENOMEM;
 220        }
 221
 222        drm_mode_set_name(mode);
 223
 224        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 225        panel->connector->display_info.width_mm = mode->width_mm;
 226        panel->connector->display_info.height_mm = mode->height_mm;
 227        drm_mode_probed_add(panel->connector, mode);
 228
 229        return 1;
 230}
 231
 232static const struct drm_panel_funcs jh057n_drm_funcs = {
 233        .disable   = jh057n_disable,
 234        .unprepare = jh057n_unprepare,
 235        .prepare   = jh057n_prepare,
 236        .enable    = jh057n_enable,
 237        .get_modes = jh057n_get_modes,
 238};
 239
 240static int allpixelson_set(void *data, u64 val)
 241{
 242        struct jh057n *ctx = data;
 243        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 244
 245        DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
 246        dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
 247        msleep(val * 1000);
 248        /* Reset the panel to get video back */
 249        drm_panel_disable(&ctx->panel);
 250        drm_panel_unprepare(&ctx->panel);
 251        drm_panel_prepare(&ctx->panel);
 252        drm_panel_enable(&ctx->panel);
 253
 254        return 0;
 255}
 256
 257DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
 258                        allpixelson_set, "%llu\n");
 259
 260static void jh057n_debugfs_init(struct jh057n *ctx)
 261{
 262        ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
 263
 264        debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
 265                            &allpixelson_fops);
 266}
 267
 268static void jh057n_debugfs_remove(struct jh057n *ctx)
 269{
 270        debugfs_remove_recursive(ctx->debugfs);
 271        ctx->debugfs = NULL;
 272}
 273
 274static int jh057n_probe(struct mipi_dsi_device *dsi)
 275{
 276        struct device *dev = &dsi->dev;
 277        struct jh057n *ctx;
 278        int ret;
 279
 280        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 281        if (!ctx)
 282                return -ENOMEM;
 283
 284        ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 285        if (IS_ERR(ctx->reset_gpio)) {
 286                DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
 287                return PTR_ERR(ctx->reset_gpio);
 288        }
 289
 290        mipi_dsi_set_drvdata(dsi, ctx);
 291
 292        ctx->dev = dev;
 293
 294        dsi->lanes = 4;
 295        dsi->format = MIPI_DSI_FMT_RGB888;
 296        dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
 297                MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
 298
 299        ctx->backlight = devm_of_find_backlight(dev);
 300        if (IS_ERR(ctx->backlight))
 301                return PTR_ERR(ctx->backlight);
 302
 303        drm_panel_init(&ctx->panel);
 304        ctx->panel.dev = dev;
 305        ctx->panel.funcs = &jh057n_drm_funcs;
 306
 307        drm_panel_add(&ctx->panel);
 308
 309        ret = mipi_dsi_attach(dsi);
 310        if (ret < 0) {
 311                DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
 312                drm_panel_remove(&ctx->panel);
 313                return ret;
 314        }
 315
 316        DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
 317                     default_mode.hdisplay, default_mode.vdisplay,
 318                     default_mode.vrefresh,
 319                     mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
 320
 321        jh057n_debugfs_init(ctx);
 322        return 0;
 323}
 324
 325static void jh057n_shutdown(struct mipi_dsi_device *dsi)
 326{
 327        struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
 328        int ret;
 329
 330        ret = jh057n_unprepare(&ctx->panel);
 331        if (ret < 0)
 332                DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
 333                              ret);
 334
 335        ret = jh057n_disable(&ctx->panel);
 336        if (ret < 0)
 337                DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
 338                              ret);
 339}
 340
 341static int jh057n_remove(struct mipi_dsi_device *dsi)
 342{
 343        struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
 344        int ret;
 345
 346        jh057n_shutdown(dsi);
 347
 348        ret = mipi_dsi_detach(dsi);
 349        if (ret < 0)
 350                DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
 351                              ret);
 352
 353        drm_panel_remove(&ctx->panel);
 354
 355        jh057n_debugfs_remove(ctx);
 356
 357        return 0;
 358}
 359
 360static const struct of_device_id jh057n_of_match[] = {
 361        { .compatible = "rocktech,jh057n00900" },
 362        { /* sentinel */ }
 363};
 364MODULE_DEVICE_TABLE(of, jh057n_of_match);
 365
 366static struct mipi_dsi_driver jh057n_driver = {
 367        .probe  = jh057n_probe,
 368        .remove = jh057n_remove,
 369        .shutdown = jh057n_shutdown,
 370        .driver = {
 371                .name = DRV_NAME,
 372                .of_match_table = jh057n_of_match,
 373        },
 374};
 375module_mipi_dsi_driver(jh057n_driver);
 376
 377MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
 378MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
 379MODULE_LICENSE("GPL v2");
 380