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>
23 #define MCS_LEVEL2_KEY 0xf0
24 #define MCS_MTP_KEY 0xf1
25 #define MCS_MTP_SET3 0xd4
27 #define MAX_BRIGHTNESS 100
28 #define DEFAULT_BRIGHTNESS 80
30 #define NUM_GAMMA_STEPS 9
31 #define GAMMA_CMD_CNT 28
33 #define FIRST_COLUMN 20
37 struct drm_panel panel
;
38 struct backlight_device
*bl_dev
;
40 struct regulator_bulk_data supplies
[2];
41 struct gpio_desc
*reset_gpio
;
44 static const struct drm_display_mode default_mode
= {
47 .hsync_start
= 320 + 1,
48 .hsync_end
= 320 + 1 + 1,
49 .htotal
= 320 + 1 + 1 + 1,
51 .vsync_start
= 320 + 150,
52 .vsync_end
= 320 + 150 + 1,
53 .vtotal
= 320 + 150 + 1 + 2,
57 static const unsigned char gamma_tbl
[NUM_GAMMA_STEPS
][GAMMA_CMD_CNT
] = {
60 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
61 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
62 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
66 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
67 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
68 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
72 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
73 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
74 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
78 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
79 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
80 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
84 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
85 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
86 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
90 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
91 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
92 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
96 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
97 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
98 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
102 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
103 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
104 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
108 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
109 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
110 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
114 static inline struct s6e63j0x03
*panel_to_s6e63j0x03(struct drm_panel
*panel
)
116 return container_of(panel
, struct s6e63j0x03
, panel
);
119 static inline ssize_t
s6e63j0x03_dcs_write_seq(struct s6e63j0x03
*ctx
,
120 const void *seq
, size_t len
)
122 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
124 return mipi_dsi_dcs_write_buffer(dsi
, seq
, len
);
127 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \
129 static const u8 d[] = { seq }; \
130 s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \
133 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03
*ctx
)
135 return s6e63j0x03_dcs_write_seq_static(ctx
, MCS_LEVEL2_KEY
, 0x5a, 0x5a);
138 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03
*ctx
, bool on
)
141 return s6e63j0x03_dcs_write_seq_static(ctx
,
142 MCS_MTP_KEY
, 0x5a, 0x5a);
144 return s6e63j0x03_dcs_write_seq_static(ctx
, MCS_MTP_KEY
, 0xa5, 0xa5);
147 static int s6e63j0x03_power_on(struct s6e63j0x03
*ctx
)
151 ret
= regulator_bulk_enable(ARRAY_SIZE(ctx
->supplies
), ctx
->supplies
);
157 gpiod_set_value(ctx
->reset_gpio
, 1);
158 usleep_range(1000, 2000);
159 gpiod_set_value(ctx
->reset_gpio
, 0);
160 usleep_range(5000, 6000);
165 static int s6e63j0x03_power_off(struct s6e63j0x03
*ctx
)
167 return regulator_bulk_disable(ARRAY_SIZE(ctx
->supplies
), ctx
->supplies
);
170 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness
)
174 index
= brightness
/ (MAX_BRIGHTNESS
/ NUM_GAMMA_STEPS
);
176 if (index
>= NUM_GAMMA_STEPS
)
177 index
= NUM_GAMMA_STEPS
- 1;
182 static int s6e63j0x03_update_gamma(struct s6e63j0x03
*ctx
,
183 unsigned int brightness
)
185 struct backlight_device
*bl_dev
= ctx
->bl_dev
;
186 unsigned int index
= s6e63j0x03_get_brightness_index(brightness
);
189 ret
= s6e63j0x03_apply_mtp_key(ctx
, true);
193 ret
= s6e63j0x03_dcs_write_seq(ctx
, gamma_tbl
[index
], GAMMA_CMD_CNT
);
197 ret
= s6e63j0x03_apply_mtp_key(ctx
, false);
201 bl_dev
->props
.brightness
= brightness
;
206 static int s6e63j0x03_set_brightness(struct backlight_device
*bl_dev
)
208 struct s6e63j0x03
*ctx
= bl_get_data(bl_dev
);
209 unsigned int brightness
= bl_dev
->props
.brightness
;
211 return s6e63j0x03_update_gamma(ctx
, brightness
);
214 static const struct backlight_ops s6e63j0x03_bl_ops
= {
215 .update_status
= s6e63j0x03_set_brightness
,
218 static int s6e63j0x03_disable(struct drm_panel
*panel
)
220 struct s6e63j0x03
*ctx
= panel_to_s6e63j0x03(panel
);
221 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
224 ret
= mipi_dsi_dcs_set_display_off(dsi
);
228 ctx
->bl_dev
->props
.power
= FB_BLANK_NORMAL
;
230 ret
= mipi_dsi_dcs_enter_sleep_mode(dsi
);
239 static int s6e63j0x03_unprepare(struct drm_panel
*panel
)
241 struct s6e63j0x03
*ctx
= panel_to_s6e63j0x03(panel
);
244 ret
= s6e63j0x03_power_off(ctx
);
248 ctx
->bl_dev
->props
.power
= FB_BLANK_POWERDOWN
;
253 static int s6e63j0x03_panel_init(struct s6e63j0x03
*ctx
)
255 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
258 ret
= s6e63j0x03_enable_lv2_command(ctx
);
262 ret
= s6e63j0x03_apply_mtp_key(ctx
, true);
266 /* set porch adjustment */
267 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xf2, 0x1c, 0x28);
272 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xb5, 0x00, 0x02, 0x00);
276 /* set caset, paset */
277 ret
= mipi_dsi_dcs_set_column_address(dsi
, FIRST_COLUMN
,
278 default_mode
.hdisplay
- 1 + FIRST_COLUMN
);
282 ret
= mipi_dsi_dcs_set_page_address(dsi
, 0, default_mode
.vdisplay
- 1);
286 /* set ltps timming 0, 1 */
287 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xf8, 0x08, 0x08, 0x08, 0x17,
288 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
292 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xf7, 0x02);
296 /* set param pos te_edge */
297 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xb0, 0x01);
301 /* set te rising edge */
302 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xe2, 0x0f);
306 /* set param pos default */
307 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xb0, 0x00);
311 ret
= mipi_dsi_dcs_exit_sleep_mode(dsi
);
315 ret
= s6e63j0x03_apply_mtp_key(ctx
, false);
322 static int s6e63j0x03_prepare(struct drm_panel
*panel
)
324 struct s6e63j0x03
*ctx
= panel_to_s6e63j0x03(panel
);
327 ret
= s6e63j0x03_power_on(ctx
);
331 ret
= s6e63j0x03_panel_init(ctx
);
335 ctx
->bl_dev
->props
.power
= FB_BLANK_NORMAL
;
340 s6e63j0x03_power_off(ctx
);
344 static int s6e63j0x03_enable(struct drm_panel
*panel
)
346 struct s6e63j0x03
*ctx
= panel_to_s6e63j0x03(panel
);
347 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
352 ret
= s6e63j0x03_apply_mtp_key(ctx
, true);
357 ret
= s6e63j0x03_dcs_write_seq_static(ctx
, 0xb1, 0x00, 0x09);
362 ret
= s6e63j0x03_dcs_write_seq_static(ctx
,
363 MIPI_DCS_SET_ADDRESS_MODE
, 0x40);
367 /* set default white brightness */
368 ret
= mipi_dsi_dcs_set_display_brightness(dsi
, 0x00ff);
373 ret
= s6e63j0x03_dcs_write_seq_static(ctx
,
374 MIPI_DCS_WRITE_CONTROL_DISPLAY
, 0x20);
379 ret
= s6e63j0x03_dcs_write_seq_static(ctx
,
380 MIPI_DCS_WRITE_POWER_SAVE
, 0x00);
384 ret
= mipi_dsi_dcs_set_tear_on(dsi
, MIPI_DSI_DCS_TEAR_MODE_VBLANK
);
388 ret
= s6e63j0x03_apply_mtp_key(ctx
, false);
392 ret
= mipi_dsi_dcs_set_display_on(dsi
);
396 ctx
->bl_dev
->props
.power
= FB_BLANK_UNBLANK
;
401 static int s6e63j0x03_get_modes(struct drm_panel
*panel
,
402 struct drm_connector
*connector
)
404 struct drm_display_mode
*mode
;
406 mode
= drm_mode_duplicate(connector
->dev
, &default_mode
);
408 dev_err(panel
->dev
, "failed to add mode %ux%u@%u\n",
409 default_mode
.hdisplay
, default_mode
.vdisplay
,
410 drm_mode_vrefresh(&default_mode
));
414 drm_mode_set_name(mode
);
416 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
417 drm_mode_probed_add(connector
, mode
);
419 connector
->display_info
.width_mm
= 29;
420 connector
->display_info
.height_mm
= 29;
425 static const struct drm_panel_funcs s6e63j0x03_funcs
= {
426 .disable
= s6e63j0x03_disable
,
427 .unprepare
= s6e63j0x03_unprepare
,
428 .prepare
= s6e63j0x03_prepare
,
429 .enable
= s6e63j0x03_enable
,
430 .get_modes
= s6e63j0x03_get_modes
,
433 static int s6e63j0x03_probe(struct mipi_dsi_device
*dsi
)
435 struct device
*dev
= &dsi
->dev
;
436 struct s6e63j0x03
*ctx
;
439 ctx
= devm_kzalloc(dev
, sizeof(struct s6e63j0x03
), GFP_KERNEL
);
443 mipi_dsi_set_drvdata(dsi
, ctx
);
448 dsi
->format
= MIPI_DSI_FMT_RGB888
;
449 dsi
->mode_flags
= MIPI_DSI_MODE_EOT_PACKET
;
451 ctx
->supplies
[0].supply
= "vdd3";
452 ctx
->supplies
[1].supply
= "vci";
453 ret
= devm_regulator_bulk_get(dev
, ARRAY_SIZE(ctx
->supplies
),
456 dev_err(dev
, "failed to get regulators: %d\n", ret
);
460 ctx
->reset_gpio
= devm_gpiod_get(dev
, "reset", GPIOD_OUT_LOW
);
461 if (IS_ERR(ctx
->reset_gpio
)) {
462 dev_err(dev
, "cannot get reset-gpio: %ld\n",
463 PTR_ERR(ctx
->reset_gpio
));
464 return PTR_ERR(ctx
->reset_gpio
);
467 drm_panel_init(&ctx
->panel
, dev
, &s6e63j0x03_funcs
,
468 DRM_MODE_CONNECTOR_DSI
);
470 ctx
->bl_dev
= backlight_device_register("s6e63j0x03", dev
, ctx
,
471 &s6e63j0x03_bl_ops
, NULL
);
472 if (IS_ERR(ctx
->bl_dev
)) {
473 dev_err(dev
, "failed to register backlight device\n");
474 return PTR_ERR(ctx
->bl_dev
);
477 ctx
->bl_dev
->props
.max_brightness
= MAX_BRIGHTNESS
;
478 ctx
->bl_dev
->props
.brightness
= DEFAULT_BRIGHTNESS
;
479 ctx
->bl_dev
->props
.power
= FB_BLANK_POWERDOWN
;
481 drm_panel_add(&ctx
->panel
);
483 ret
= mipi_dsi_attach(dsi
);
490 drm_panel_remove(&ctx
->panel
);
491 backlight_device_unregister(ctx
->bl_dev
);
496 static int s6e63j0x03_remove(struct mipi_dsi_device
*dsi
)
498 struct s6e63j0x03
*ctx
= mipi_dsi_get_drvdata(dsi
);
500 mipi_dsi_detach(dsi
);
501 drm_panel_remove(&ctx
->panel
);
503 backlight_device_unregister(ctx
->bl_dev
);
508 static const struct of_device_id s6e63j0x03_of_match
[] = {
509 { .compatible
= "samsung,s6e63j0x03" },
512 MODULE_DEVICE_TABLE(of
, s6e63j0x03_of_match
);
514 static struct mipi_dsi_driver s6e63j0x03_driver
= {
515 .probe
= s6e63j0x03_probe
,
516 .remove
= s6e63j0x03_remove
,
518 .name
= "panel_samsung_s6e63j0x03",
519 .of_match_table
= s6e63j0x03_of_match
,
522 module_mipi_dsi_driver(s6e63j0x03_driver
);
524 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
525 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
526 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
527 MODULE_LICENSE("GPL v2");