0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/backlight.h>
0012 #include <linux/delay.h>
0013 #include <linux/gpio/consumer.h>
0014 #include <linux/module.h>
0015 #include <linux/regulator/consumer.h>
0016
0017 #include <video/mipi_display.h>
0018
0019 #include <drm/drm_mipi_dsi.h>
0020 #include <drm/drm_modes.h>
0021 #include <drm/drm_panel.h>
0022
0023 #define MCS_LEVEL2_KEY 0xf0
0024 #define MCS_MTP_KEY 0xf1
0025 #define MCS_MTP_SET3 0xd4
0026
0027 #define MAX_BRIGHTNESS 100
0028 #define DEFAULT_BRIGHTNESS 80
0029
0030 #define NUM_GAMMA_STEPS 9
0031 #define GAMMA_CMD_CNT 28
0032
0033 #define FIRST_COLUMN 20
0034
0035 struct s6e63j0x03 {
0036 struct device *dev;
0037 struct drm_panel panel;
0038 struct backlight_device *bl_dev;
0039
0040 struct regulator_bulk_data supplies[2];
0041 struct gpio_desc *reset_gpio;
0042 };
0043
0044 static const struct drm_display_mode default_mode = {
0045 .clock = 4649,
0046 .hdisplay = 320,
0047 .hsync_start = 320 + 1,
0048 .hsync_end = 320 + 1 + 1,
0049 .htotal = 320 + 1 + 1 + 1,
0050 .vdisplay = 320,
0051 .vsync_start = 320 + 150,
0052 .vsync_end = 320 + 150 + 1,
0053 .vtotal = 320 + 150 + 1 + 2,
0054 .flags = 0,
0055 };
0056
0057 static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
0058 {
0059 MCS_MTP_SET3,
0060 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
0061 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
0062 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
0063 },
0064 {
0065 MCS_MTP_SET3,
0066 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
0067 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
0068 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
0069 },
0070 {
0071 MCS_MTP_SET3,
0072 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
0073 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
0074 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
0075 },
0076 {
0077 MCS_MTP_SET3,
0078 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
0079 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
0080 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
0081 },
0082 {
0083 MCS_MTP_SET3,
0084 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
0085 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
0086 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
0087 },
0088 {
0089 MCS_MTP_SET3,
0090 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
0091 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
0092 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
0093 },
0094 {
0095 MCS_MTP_SET3,
0096 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
0097 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
0098 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
0099 },
0100 {
0101 MCS_MTP_SET3,
0102 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
0103 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
0104 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
0105 },
0106 {
0107 MCS_MTP_SET3,
0108 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
0109 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
0110 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
0111 }
0112 };
0113
0114 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
0115 {
0116 return container_of(panel, struct s6e63j0x03, panel);
0117 }
0118
0119 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
0120 const void *seq, size_t len)
0121 {
0122 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0123
0124 return mipi_dsi_dcs_write_buffer(dsi, seq, len);
0125 }
0126
0127 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \
0128 ({ \
0129 static const u8 d[] = { seq }; \
0130 s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \
0131 })
0132
0133 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
0134 {
0135 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
0136 }
0137
0138 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
0139 {
0140 if (on)
0141 return s6e63j0x03_dcs_write_seq_static(ctx,
0142 MCS_MTP_KEY, 0x5a, 0x5a);
0143
0144 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
0145 }
0146
0147 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
0148 {
0149 int ret;
0150
0151 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0152 if (ret < 0)
0153 return ret;
0154
0155 msleep(30);
0156
0157 gpiod_set_value(ctx->reset_gpio, 1);
0158 usleep_range(1000, 2000);
0159 gpiod_set_value(ctx->reset_gpio, 0);
0160 usleep_range(5000, 6000);
0161
0162 return 0;
0163 }
0164
0165 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
0166 {
0167 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0168 }
0169
0170 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
0171 {
0172 unsigned int index;
0173
0174 index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
0175
0176 if (index >= NUM_GAMMA_STEPS)
0177 index = NUM_GAMMA_STEPS - 1;
0178
0179 return index;
0180 }
0181
0182 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
0183 unsigned int brightness)
0184 {
0185 struct backlight_device *bl_dev = ctx->bl_dev;
0186 unsigned int index = s6e63j0x03_get_brightness_index(brightness);
0187 int ret;
0188
0189 ret = s6e63j0x03_apply_mtp_key(ctx, true);
0190 if (ret < 0)
0191 return ret;
0192
0193 ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
0194 if (ret < 0)
0195 return ret;
0196
0197 ret = s6e63j0x03_apply_mtp_key(ctx, false);
0198 if (ret < 0)
0199 return ret;
0200
0201 bl_dev->props.brightness = brightness;
0202
0203 return 0;
0204 }
0205
0206 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
0207 {
0208 struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
0209 unsigned int brightness = bl_dev->props.brightness;
0210
0211 return s6e63j0x03_update_gamma(ctx, brightness);
0212 }
0213
0214 static const struct backlight_ops s6e63j0x03_bl_ops = {
0215 .update_status = s6e63j0x03_set_brightness,
0216 };
0217
0218 static int s6e63j0x03_disable(struct drm_panel *panel)
0219 {
0220 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0221 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0222 int ret;
0223
0224 ret = mipi_dsi_dcs_set_display_off(dsi);
0225 if (ret < 0)
0226 return ret;
0227
0228 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
0229
0230 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0231 if (ret < 0)
0232 return ret;
0233
0234 msleep(120);
0235
0236 return 0;
0237 }
0238
0239 static int s6e63j0x03_unprepare(struct drm_panel *panel)
0240 {
0241 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0242 int ret;
0243
0244 ret = s6e63j0x03_power_off(ctx);
0245 if (ret < 0)
0246 return ret;
0247
0248 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
0249
0250 return 0;
0251 }
0252
0253 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
0254 {
0255 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0256 int ret;
0257
0258 ret = s6e63j0x03_enable_lv2_command(ctx);
0259 if (ret < 0)
0260 return ret;
0261
0262 ret = s6e63j0x03_apply_mtp_key(ctx, true);
0263 if (ret < 0)
0264 return ret;
0265
0266
0267 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
0268 if (ret < 0)
0269 return ret;
0270
0271
0272 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
0273 if (ret < 0)
0274 return ret;
0275
0276
0277 ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
0278 default_mode.hdisplay - 1 + FIRST_COLUMN);
0279 if (ret < 0)
0280 return ret;
0281
0282 ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
0283 if (ret < 0)
0284 return ret;
0285
0286
0287 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
0288 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
0289 if (ret < 0)
0290 return ret;
0291
0292 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
0293 if (ret < 0)
0294 return ret;
0295
0296
0297 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
0298 if (ret < 0)
0299 return ret;
0300
0301
0302 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
0303 if (ret < 0)
0304 return ret;
0305
0306
0307 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
0308 if (ret < 0)
0309 return ret;
0310
0311 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0312 if (ret < 0)
0313 return ret;
0314
0315 ret = s6e63j0x03_apply_mtp_key(ctx, false);
0316 if (ret < 0)
0317 return ret;
0318
0319 return 0;
0320 }
0321
0322 static int s6e63j0x03_prepare(struct drm_panel *panel)
0323 {
0324 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0325 int ret;
0326
0327 ret = s6e63j0x03_power_on(ctx);
0328 if (ret < 0)
0329 return ret;
0330
0331 ret = s6e63j0x03_panel_init(ctx);
0332 if (ret < 0)
0333 goto err;
0334
0335 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
0336
0337 return 0;
0338
0339 err:
0340 s6e63j0x03_power_off(ctx);
0341 return ret;
0342 }
0343
0344 static int s6e63j0x03_enable(struct drm_panel *panel)
0345 {
0346 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0347 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0348 int ret;
0349
0350 msleep(120);
0351
0352 ret = s6e63j0x03_apply_mtp_key(ctx, true);
0353 if (ret < 0)
0354 return ret;
0355
0356
0357 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
0358 if (ret < 0)
0359 return ret;
0360
0361
0362 ret = s6e63j0x03_dcs_write_seq_static(ctx,
0363 MIPI_DCS_SET_ADDRESS_MODE, 0x40);
0364 if (ret < 0)
0365 return ret;
0366
0367
0368 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
0369 if (ret < 0)
0370 return ret;
0371
0372
0373 ret = s6e63j0x03_dcs_write_seq_static(ctx,
0374 MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
0375 if (ret < 0)
0376 return ret;
0377
0378
0379 ret = s6e63j0x03_dcs_write_seq_static(ctx,
0380 MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0381 if (ret < 0)
0382 return ret;
0383
0384 ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
0385 if (ret < 0)
0386 return ret;
0387
0388 ret = s6e63j0x03_apply_mtp_key(ctx, false);
0389 if (ret < 0)
0390 return ret;
0391
0392 ret = mipi_dsi_dcs_set_display_on(dsi);
0393 if (ret < 0)
0394 return ret;
0395
0396 ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
0397
0398 return 0;
0399 }
0400
0401 static int s6e63j0x03_get_modes(struct drm_panel *panel,
0402 struct drm_connector *connector)
0403 {
0404 struct drm_display_mode *mode;
0405
0406 mode = drm_mode_duplicate(connector->dev, &default_mode);
0407 if (!mode) {
0408 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0409 default_mode.hdisplay, default_mode.vdisplay,
0410 drm_mode_vrefresh(&default_mode));
0411 return -ENOMEM;
0412 }
0413
0414 drm_mode_set_name(mode);
0415
0416 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0417 drm_mode_probed_add(connector, mode);
0418
0419 connector->display_info.width_mm = 29;
0420 connector->display_info.height_mm = 29;
0421
0422 return 1;
0423 }
0424
0425 static const struct drm_panel_funcs s6e63j0x03_funcs = {
0426 .disable = s6e63j0x03_disable,
0427 .unprepare = s6e63j0x03_unprepare,
0428 .prepare = s6e63j0x03_prepare,
0429 .enable = s6e63j0x03_enable,
0430 .get_modes = s6e63j0x03_get_modes,
0431 };
0432
0433 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
0434 {
0435 struct device *dev = &dsi->dev;
0436 struct s6e63j0x03 *ctx;
0437 int ret;
0438
0439 ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
0440 if (!ctx)
0441 return -ENOMEM;
0442
0443 mipi_dsi_set_drvdata(dsi, ctx);
0444
0445 ctx->dev = dev;
0446
0447 dsi->lanes = 1;
0448 dsi->format = MIPI_DSI_FMT_RGB888;
0449 dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET;
0450
0451 ctx->supplies[0].supply = "vdd3";
0452 ctx->supplies[1].supply = "vci";
0453 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
0454 ctx->supplies);
0455 if (ret < 0)
0456 return dev_err_probe(dev, ret, "failed to get regulators\n");
0457
0458 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
0459 if (IS_ERR(ctx->reset_gpio))
0460 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
0461 "cannot get reset-gpio\n");
0462
0463 drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,
0464 DRM_MODE_CONNECTOR_DSI);
0465
0466 ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
0467 &s6e63j0x03_bl_ops, NULL);
0468 if (IS_ERR(ctx->bl_dev))
0469 return dev_err_probe(dev, PTR_ERR(ctx->bl_dev),
0470 "failed to register backlight device\n");
0471
0472 ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
0473 ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
0474 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
0475
0476 drm_panel_add(&ctx->panel);
0477
0478 ret = mipi_dsi_attach(dsi);
0479 if (ret < 0)
0480 goto remove_panel;
0481
0482 return ret;
0483
0484 remove_panel:
0485 drm_panel_remove(&ctx->panel);
0486 backlight_device_unregister(ctx->bl_dev);
0487
0488 return ret;
0489 }
0490
0491 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
0492 {
0493 struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
0494
0495 mipi_dsi_detach(dsi);
0496 drm_panel_remove(&ctx->panel);
0497
0498 backlight_device_unregister(ctx->bl_dev);
0499
0500 return 0;
0501 }
0502
0503 static const struct of_device_id s6e63j0x03_of_match[] = {
0504 { .compatible = "samsung,s6e63j0x03" },
0505 { }
0506 };
0507 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
0508
0509 static struct mipi_dsi_driver s6e63j0x03_driver = {
0510 .probe = s6e63j0x03_probe,
0511 .remove = s6e63j0x03_remove,
0512 .driver = {
0513 .name = "panel_samsung_s6e63j0x03",
0514 .of_match_table = s6e63j0x03_of_match,
0515 },
0516 };
0517 module_mipi_dsi_driver(s6e63j0x03_driver);
0518
0519 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
0520 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
0521 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
0522 MODULE_LICENSE("GPL v2");