1 // SPDX-License-Identifier: GPL-2.0-only
3 * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
5 * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
7 * Inki Dae <inki.dae@samsung.com>
8 * Hoegeun Kwon <hoegeun.kwon@samsung.com>
11 #include <linux/backlight.h>
12 #include <linux/delay.h>
13 #include <linux/gpio/consumer.h>
14 #include <linux/module.h>
15 #include <linux/regulator/consumer.h>
17 #include <video/mipi_display.h>
19 #include <drm/drm_mipi_dsi.h>
20 #include <drm/drm_modes.h>
21 #include <drm/drm_panel.h>
22 #include <drm/drm_print.h>
24 #define MCS_LEVEL2_KEY 0xf0
25 #define MCS_MTP_KEY 0xf1
26 #define MCS_MTP_SET3 0xd4
28 #define MAX_BRIGHTNESS 100
29 #define DEFAULT_BRIGHTNESS 80
31 #define NUM_GAMMA_STEPS 9
32 #define GAMMA_CMD_CNT 28
34 #define FIRST_COLUMN 20
38 struct drm_panel panel;
39 struct backlight_device *bl_dev;
41 struct regulator_bulk_data supplies[2];
42 struct gpio_desc *reset_gpio;
45 static const struct drm_display_mode default_mode = {
48 .hsync_start = 320 + 1,
49 .hsync_end = 320 + 1 + 1,
50 .htotal = 320 + 1 + 1 + 1,
52 .vsync_start = 320 + 150,
53 .vsync_end = 320 + 150 + 1,
54 .vtotal = 320 + 150 + 1 + 2,
59 static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
62 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
63 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
64 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
68 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
69 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
70 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
74 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
75 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
76 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
80 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
81 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
82 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
86 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
87 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
88 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
92 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
93 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
94 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
98 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
99 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
100 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
104 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
105 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
106 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
110 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
111 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
112 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
116 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
118 return container_of(panel, struct s6e63j0x03, panel);
121 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
122 const void *seq, size_t len)
124 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
126 return mipi_dsi_dcs_write_buffer(dsi, seq, len);
129 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \
131 static const u8 d[] = { seq }; \
132 s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \
135 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
137 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
140 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
143 return s6e63j0x03_dcs_write_seq_static(ctx,
144 MCS_MTP_KEY, 0x5a, 0x5a);
146 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
149 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
153 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
159 gpiod_set_value(ctx->reset_gpio, 1);
160 usleep_range(1000, 2000);
161 gpiod_set_value(ctx->reset_gpio, 0);
162 usleep_range(5000, 6000);
167 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
169 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
172 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
176 index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
178 if (index >= NUM_GAMMA_STEPS)
179 index = NUM_GAMMA_STEPS - 1;
184 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
185 unsigned int brightness)
187 struct backlight_device *bl_dev = ctx->bl_dev;
188 unsigned int index = s6e63j0x03_get_brightness_index(brightness);
191 ret = s6e63j0x03_apply_mtp_key(ctx, true);
195 ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
199 ret = s6e63j0x03_apply_mtp_key(ctx, false);
203 bl_dev->props.brightness = brightness;
208 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
210 struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
211 unsigned int brightness = bl_dev->props.brightness;
213 return s6e63j0x03_update_gamma(ctx, brightness);
216 static const struct backlight_ops s6e63j0x03_bl_ops = {
217 .update_status = s6e63j0x03_set_brightness,
220 static int s6e63j0x03_disable(struct drm_panel *panel)
222 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
223 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
226 ret = mipi_dsi_dcs_set_display_off(dsi);
230 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
232 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
241 static int s6e63j0x03_unprepare(struct drm_panel *panel)
243 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
246 ret = s6e63j0x03_power_off(ctx);
250 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
255 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
257 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
260 ret = s6e63j0x03_enable_lv2_command(ctx);
264 ret = s6e63j0x03_apply_mtp_key(ctx, true);
268 /* set porch adjustment */
269 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
274 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
278 /* set caset, paset */
279 ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
280 default_mode.hdisplay - 1 + FIRST_COLUMN);
284 ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
288 /* set ltps timming 0, 1 */
289 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
290 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
294 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
298 /* set param pos te_edge */
299 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
303 /* set te rising edge */
304 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
308 /* set param pos default */
309 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
313 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
317 ret = s6e63j0x03_apply_mtp_key(ctx, false);
324 static int s6e63j0x03_prepare(struct drm_panel *panel)
326 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
329 ret = s6e63j0x03_power_on(ctx);
333 ret = s6e63j0x03_panel_init(ctx);
337 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
342 s6e63j0x03_power_off(ctx);
346 static int s6e63j0x03_enable(struct drm_panel *panel)
348 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
349 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
354 ret = s6e63j0x03_apply_mtp_key(ctx, true);
359 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
364 ret = s6e63j0x03_dcs_write_seq_static(ctx,
365 MIPI_DCS_SET_ADDRESS_MODE, 0x40);
369 /* set default white brightness */
370 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
375 ret = s6e63j0x03_dcs_write_seq_static(ctx,
376 MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
381 ret = s6e63j0x03_dcs_write_seq_static(ctx,
382 MIPI_DCS_WRITE_POWER_SAVE, 0x00);
386 ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
390 ret = s6e63j0x03_apply_mtp_key(ctx, false);
394 ret = mipi_dsi_dcs_set_display_on(dsi);
398 ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
403 static int s6e63j0x03_get_modes(struct drm_panel *panel,
404 struct drm_connector *connector)
406 struct drm_display_mode *mode;
408 mode = drm_mode_duplicate(connector->dev, &default_mode);
410 DRM_ERROR("failed to add mode %ux%ux@%u\n",
411 default_mode.hdisplay, default_mode.vdisplay,
412 default_mode.vrefresh);
416 drm_mode_set_name(mode);
418 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
419 drm_mode_probed_add(connector, mode);
421 connector->display_info.width_mm = 29;
422 connector->display_info.height_mm = 29;
427 static const struct drm_panel_funcs s6e63j0x03_funcs = {
428 .disable = s6e63j0x03_disable,
429 .unprepare = s6e63j0x03_unprepare,
430 .prepare = s6e63j0x03_prepare,
431 .enable = s6e63j0x03_enable,
432 .get_modes = s6e63j0x03_get_modes,
435 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
437 struct device *dev = &dsi->dev;
438 struct s6e63j0x03 *ctx;
441 ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
445 mipi_dsi_set_drvdata(dsi, ctx);
450 dsi->format = MIPI_DSI_FMT_RGB888;
451 dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
453 ctx->supplies[0].supply = "vdd3";
454 ctx->supplies[1].supply = "vci";
455 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
458 dev_err(dev, "failed to get regulators: %d\n", ret);
462 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
463 if (IS_ERR(ctx->reset_gpio)) {
464 dev_err(dev, "cannot get reset-gpio: %ld\n",
465 PTR_ERR(ctx->reset_gpio));
466 return PTR_ERR(ctx->reset_gpio);
469 drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,
470 DRM_MODE_CONNECTOR_DSI);
472 ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
473 &s6e63j0x03_bl_ops, NULL);
474 if (IS_ERR(ctx->bl_dev)) {
475 dev_err(dev, "failed to register backlight device\n");
476 return PTR_ERR(ctx->bl_dev);
479 ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
480 ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
481 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
483 ret = drm_panel_add(&ctx->panel);
485 goto unregister_backlight;
487 ret = mipi_dsi_attach(dsi);
494 drm_panel_remove(&ctx->panel);
496 unregister_backlight:
497 backlight_device_unregister(ctx->bl_dev);
502 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
504 struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
506 mipi_dsi_detach(dsi);
507 drm_panel_remove(&ctx->panel);
509 backlight_device_unregister(ctx->bl_dev);
514 static const struct of_device_id s6e63j0x03_of_match[] = {
515 { .compatible = "samsung,s6e63j0x03" },
518 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
520 static struct mipi_dsi_driver s6e63j0x03_driver = {
521 .probe = s6e63j0x03_probe,
522 .remove = s6e63j0x03_remove,
524 .name = "panel_samsung_s6e63j0x03",
525 .of_match_table = s6e63j0x03_of_match,
528 module_mipi_dsi_driver(s6e63j0x03_driver);
530 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
531 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
532 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
533 MODULE_LICENSE("GPL v2");