linux/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include <linux/backlight.h>
   4#include <linux/delay.h>
   5#include <linux/gpio/consumer.h>
   6#include <linux/module.h>
   7#include <linux/of.h>
   8#include <linux/regulator/consumer.h>
   9
  10#include <drm/drm_mipi_dsi.h>
  11#include <drm/drm_modes.h>
  12#include <drm/drm_panel.h>
  13
  14struct tm5p5_nt35596 {
  15        struct drm_panel panel;
  16        struct mipi_dsi_device *dsi;
  17        struct regulator_bulk_data supplies[2];
  18        struct gpio_desc *reset_gpio;
  19        bool prepared;
  20};
  21
  22static inline struct tm5p5_nt35596 *to_tm5p5_nt35596(struct drm_panel *panel)
  23{
  24        return container_of(panel, struct tm5p5_nt35596, panel);
  25}
  26
  27#define dsi_generic_write_seq(dsi, seq...) do {                         \
  28                static const u8 d[] = { seq };                          \
  29                int ret;                                                \
  30                ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d));    \
  31                if (ret < 0)                                            \
  32                        return ret;                                     \
  33        } while (0)
  34
  35#define dsi_dcs_write_seq(dsi, seq...) do {                             \
  36                static const u8 d[] = { seq };                          \
  37                int ret;                                                \
  38                ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
  39                if (ret < 0)                                            \
  40                        return ret;                                     \
  41        } while (0)
  42
  43static void tm5p5_nt35596_reset(struct tm5p5_nt35596 *ctx)
  44{
  45        gpiod_set_value_cansleep(ctx->reset_gpio, 1);
  46        usleep_range(1000, 2000);
  47        gpiod_set_value_cansleep(ctx->reset_gpio, 0);
  48        usleep_range(1000, 2000);
  49        gpiod_set_value_cansleep(ctx->reset_gpio, 1);
  50        usleep_range(15000, 16000);
  51}
  52
  53static int tm5p5_nt35596_on(struct tm5p5_nt35596 *ctx)
  54{
  55        struct mipi_dsi_device *dsi = ctx->dsi;
  56
  57        dsi_generic_write_seq(dsi, 0xff, 0x05);
  58        dsi_generic_write_seq(dsi, 0xfb, 0x01);
  59        dsi_generic_write_seq(dsi, 0xc5, 0x31);
  60        dsi_generic_write_seq(dsi, 0xff, 0x04);
  61        dsi_generic_write_seq(dsi, 0x01, 0x84);
  62        dsi_generic_write_seq(dsi, 0x05, 0x25);
  63        dsi_generic_write_seq(dsi, 0x06, 0x01);
  64        dsi_generic_write_seq(dsi, 0x07, 0x20);
  65        dsi_generic_write_seq(dsi, 0x08, 0x06);
  66        dsi_generic_write_seq(dsi, 0x09, 0x08);
  67        dsi_generic_write_seq(dsi, 0x0a, 0x10);
  68        dsi_generic_write_seq(dsi, 0x0b, 0x10);
  69        dsi_generic_write_seq(dsi, 0x0c, 0x10);
  70        dsi_generic_write_seq(dsi, 0x0d, 0x14);
  71        dsi_generic_write_seq(dsi, 0x0e, 0x14);
  72        dsi_generic_write_seq(dsi, 0x0f, 0x14);
  73        dsi_generic_write_seq(dsi, 0x10, 0x14);
  74        dsi_generic_write_seq(dsi, 0x11, 0x14);
  75        dsi_generic_write_seq(dsi, 0x12, 0x14);
  76        dsi_generic_write_seq(dsi, 0x17, 0xf3);
  77        dsi_generic_write_seq(dsi, 0x18, 0xc0);
  78        dsi_generic_write_seq(dsi, 0x19, 0xc0);
  79        dsi_generic_write_seq(dsi, 0x1a, 0xc0);
  80        dsi_generic_write_seq(dsi, 0x1b, 0xb3);
  81        dsi_generic_write_seq(dsi, 0x1c, 0xb3);
  82        dsi_generic_write_seq(dsi, 0x1d, 0xb3);
  83        dsi_generic_write_seq(dsi, 0x1e, 0xb3);
  84        dsi_generic_write_seq(dsi, 0x1f, 0xb3);
  85        dsi_generic_write_seq(dsi, 0x20, 0xb3);
  86        dsi_generic_write_seq(dsi, 0xfb, 0x01);
  87        dsi_generic_write_seq(dsi, 0xff, 0x00);
  88        dsi_generic_write_seq(dsi, 0xfb, 0x01);
  89        dsi_generic_write_seq(dsi, 0x35, 0x01);
  90        dsi_generic_write_seq(dsi, 0xd3, 0x06);
  91        dsi_generic_write_seq(dsi, 0xd4, 0x04);
  92        dsi_generic_write_seq(dsi, 0x5e, 0x0d);
  93        dsi_generic_write_seq(dsi, 0x11, 0x00);
  94        msleep(100);
  95        dsi_generic_write_seq(dsi, 0x29, 0x00);
  96        dsi_generic_write_seq(dsi, 0x53, 0x24);
  97
  98        return 0;
  99}
 100
 101static int tm5p5_nt35596_off(struct tm5p5_nt35596 *ctx)
 102{
 103        struct mipi_dsi_device *dsi = ctx->dsi;
 104        struct device *dev = &dsi->dev;
 105        int ret;
 106
 107        ret = mipi_dsi_dcs_set_display_off(dsi);
 108        if (ret < 0) {
 109                dev_err(dev, "Failed to set display off: %d\n", ret);
 110                return ret;
 111        }
 112        msleep(60);
 113
 114        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
 115        if (ret < 0) {
 116                dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
 117                return ret;
 118        }
 119
 120        dsi_dcs_write_seq(dsi, 0x4f, 0x01);
 121
 122        return 0;
 123}
 124
 125static int tm5p5_nt35596_prepare(struct drm_panel *panel)
 126{
 127        struct tm5p5_nt35596 *ctx = to_tm5p5_nt35596(panel);
 128        struct device *dev = &ctx->dsi->dev;
 129        int ret;
 130
 131        if (ctx->prepared)
 132                return 0;
 133
 134        ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 135        if (ret < 0) {
 136                dev_err(dev, "Failed to enable regulators: %d\n", ret);
 137                return ret;
 138        }
 139
 140        tm5p5_nt35596_reset(ctx);
 141
 142        ret = tm5p5_nt35596_on(ctx);
 143        if (ret < 0) {
 144                dev_err(dev, "Failed to initialize panel: %d\n", ret);
 145                gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 146                regulator_bulk_disable(ARRAY_SIZE(ctx->supplies),
 147                                       ctx->supplies);
 148                return ret;
 149        }
 150
 151        ctx->prepared = true;
 152        return 0;
 153}
 154
 155static int tm5p5_nt35596_unprepare(struct drm_panel *panel)
 156{
 157        struct tm5p5_nt35596 *ctx = to_tm5p5_nt35596(panel);
 158        struct device *dev = &ctx->dsi->dev;
 159        int ret;
 160
 161        if (!ctx->prepared)
 162                return 0;
 163
 164        ret = tm5p5_nt35596_off(ctx);
 165        if (ret < 0)
 166                dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
 167
 168        gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 169        regulator_bulk_disable(ARRAY_SIZE(ctx->supplies),
 170                               ctx->supplies);
 171
 172        ctx->prepared = false;
 173        return 0;
 174}
 175
 176static const struct drm_display_mode tm5p5_nt35596_mode = {
 177        .clock = (1080 + 100 + 8 + 16) * (1920 + 4 + 2 + 4) * 60 / 1000,
 178        .hdisplay = 1080,
 179        .hsync_start = 1080 + 100,
 180        .hsync_end = 1080 + 100 + 8,
 181        .htotal = 1080 + 100 + 8 + 16,
 182        .vdisplay = 1920,
 183        .vsync_start = 1920 + 4,
 184        .vsync_end = 1920 + 4 + 2,
 185        .vtotal = 1920 + 4 + 2 + 4,
 186        .width_mm = 68,
 187        .height_mm = 121,
 188};
 189
 190static int tm5p5_nt35596_get_modes(struct drm_panel *panel,
 191                                   struct drm_connector *connector)
 192{
 193        struct drm_display_mode *mode;
 194
 195        mode = drm_mode_duplicate(connector->dev, &tm5p5_nt35596_mode);
 196        if (!mode)
 197                return -ENOMEM;
 198
 199        drm_mode_set_name(mode);
 200
 201        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 202        connector->display_info.width_mm = mode->width_mm;
 203        connector->display_info.height_mm = mode->height_mm;
 204        drm_mode_probed_add(connector, mode);
 205
 206        return 1;
 207}
 208
 209static const struct drm_panel_funcs tm5p5_nt35596_panel_funcs = {
 210        .prepare = tm5p5_nt35596_prepare,
 211        .unprepare = tm5p5_nt35596_unprepare,
 212        .get_modes = tm5p5_nt35596_get_modes,
 213};
 214
 215static int tm5p5_nt35596_bl_update_status(struct backlight_device *bl)
 216{
 217        struct mipi_dsi_device *dsi = bl_get_data(bl);
 218        u16 brightness = bl->props.brightness;
 219        int ret;
 220
 221        if (bl->props.power != FB_BLANK_UNBLANK ||
 222            bl->props.fb_blank != FB_BLANK_UNBLANK ||
 223            bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
 224                brightness = 0;
 225
 226        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 227
 228        ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
 229        if (ret < 0)
 230                return ret;
 231
 232        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 233
 234        return 0;
 235}
 236
 237static int tm5p5_nt35596_bl_get_brightness(struct backlight_device *bl)
 238{
 239        struct mipi_dsi_device *dsi = bl_get_data(bl);
 240        u16 brightness = bl->props.brightness;
 241        int ret;
 242
 243        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 244
 245        ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
 246        if (ret < 0)
 247                return ret;
 248
 249        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 250
 251        return brightness & 0xff;
 252}
 253
 254static const struct backlight_ops tm5p5_nt35596_bl_ops = {
 255        .update_status = tm5p5_nt35596_bl_update_status,
 256        .get_brightness = tm5p5_nt35596_bl_get_brightness,
 257};
 258
 259static struct backlight_device *
 260tm5p5_nt35596_create_backlight(struct mipi_dsi_device *dsi)
 261{
 262        struct device *dev = &dsi->dev;
 263        const struct backlight_properties props = {
 264                .type = BACKLIGHT_RAW,
 265                .brightness = 255,
 266                .max_brightness = 255,
 267        };
 268
 269        return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
 270                                              &tm5p5_nt35596_bl_ops, &props);
 271}
 272
 273static int tm5p5_nt35596_probe(struct mipi_dsi_device *dsi)
 274{
 275        struct device *dev = &dsi->dev;
 276        struct tm5p5_nt35596 *ctx;
 277        int ret;
 278
 279        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 280        if (!ctx)
 281                return -ENOMEM;
 282
 283        ctx->supplies[0].supply = "vdd";
 284        ctx->supplies[1].supply = "vddio";
 285        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
 286                                      ctx->supplies);
 287        if (ret < 0) {
 288                dev_err(dev, "Failed to get regulators: %d\n", ret);
 289                return ret;
 290        }
 291
 292        ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 293        if (IS_ERR(ctx->reset_gpio)) {
 294                ret = PTR_ERR(ctx->reset_gpio);
 295                dev_err(dev, "Failed to get reset-gpios: %d\n", ret);
 296                return ret;
 297        }
 298
 299        ctx->dsi = dsi;
 300        mipi_dsi_set_drvdata(dsi, ctx);
 301
 302        dsi->lanes = 4;
 303        dsi->format = MIPI_DSI_FMT_RGB888;
 304        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 305                          MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_NO_EOT_PACKET |
 306                          MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
 307
 308        drm_panel_init(&ctx->panel, dev, &tm5p5_nt35596_panel_funcs,
 309                       DRM_MODE_CONNECTOR_DSI);
 310
 311        ctx->panel.backlight = tm5p5_nt35596_create_backlight(dsi);
 312        if (IS_ERR(ctx->panel.backlight)) {
 313                ret = PTR_ERR(ctx->panel.backlight);
 314                dev_err(dev, "Failed to create backlight: %d\n", ret);
 315                return ret;
 316        }
 317
 318        drm_panel_add(&ctx->panel);
 319
 320        ret = mipi_dsi_attach(dsi);
 321        if (ret < 0) {
 322                dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
 323                return ret;
 324        }
 325
 326        return 0;
 327}
 328
 329static int tm5p5_nt35596_remove(struct mipi_dsi_device *dsi)
 330{
 331        struct tm5p5_nt35596 *ctx = mipi_dsi_get_drvdata(dsi);
 332        int ret;
 333
 334        ret = mipi_dsi_detach(dsi);
 335        if (ret < 0)
 336                dev_err(&dsi->dev,
 337                        "Failed to detach from DSI host: %d\n", ret);
 338
 339        drm_panel_remove(&ctx->panel);
 340
 341        return 0;
 342}
 343
 344static const struct of_device_id tm5p5_nt35596_of_match[] = {
 345        { .compatible = "asus,z00t-tm5p5-n35596" },
 346        { /* sentinel */ }
 347};
 348MODULE_DEVICE_TABLE(of, tm5p5_nt35596_of_match);
 349
 350static struct mipi_dsi_driver tm5p5_nt35596_driver = {
 351        .probe = tm5p5_nt35596_probe,
 352        .remove = tm5p5_nt35596_remove,
 353        .driver = {
 354                .name = "panel-tm5p5-nt35596",
 355                .of_match_table = tm5p5_nt35596_of_match,
 356        },
 357};
 358module_mipi_dsi_driver(tm5p5_nt35596_driver);
 359
 360MODULE_AUTHOR("Konrad Dybcio <konradybcio@gmail.com>");
 361MODULE_DESCRIPTION("DRM driver for tm5p5 nt35596 1080p video mode dsi panel");
 362MODULE_LICENSE("GPL v2");
 363