1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22a05177dSHoegeun Kwon /* 32a05177dSHoegeun Kwon * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver. 42a05177dSHoegeun Kwon * 52a05177dSHoegeun Kwon * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd 62a05177dSHoegeun Kwon * 72a05177dSHoegeun Kwon * Inki Dae <inki.dae@samsung.com> 82a05177dSHoegeun Kwon * Hoegeun Kwon <hoegeun.kwon@samsung.com> 92a05177dSHoegeun Kwon */ 102a05177dSHoegeun Kwon 112a05177dSHoegeun Kwon #include <drm/drmP.h> 122a05177dSHoegeun Kwon #include <drm/drm_mipi_dsi.h> 132a05177dSHoegeun Kwon #include <drm/drm_panel.h> 142a05177dSHoegeun Kwon #include <linux/backlight.h> 152a05177dSHoegeun Kwon #include <linux/gpio/consumer.h> 162a05177dSHoegeun Kwon #include <linux/regulator/consumer.h> 172a05177dSHoegeun Kwon #include <video/mipi_display.h> 182a05177dSHoegeun Kwon 192a05177dSHoegeun Kwon #define MCS_LEVEL2_KEY 0xf0 202a05177dSHoegeun Kwon #define MCS_MTP_KEY 0xf1 212a05177dSHoegeun Kwon #define MCS_MTP_SET3 0xd4 222a05177dSHoegeun Kwon 232a05177dSHoegeun Kwon #define MAX_BRIGHTNESS 100 242a05177dSHoegeun Kwon #define DEFAULT_BRIGHTNESS 80 252a05177dSHoegeun Kwon 262a05177dSHoegeun Kwon #define NUM_GAMMA_STEPS 9 272a05177dSHoegeun Kwon #define GAMMA_CMD_CNT 28 282a05177dSHoegeun Kwon 292a05177dSHoegeun Kwon #define FIRST_COLUMN 20 302a05177dSHoegeun Kwon 312a05177dSHoegeun Kwon struct s6e63j0x03 { 322a05177dSHoegeun Kwon struct device *dev; 332a05177dSHoegeun Kwon struct drm_panel panel; 342a05177dSHoegeun Kwon struct backlight_device *bl_dev; 352a05177dSHoegeun Kwon 362a05177dSHoegeun Kwon struct regulator_bulk_data supplies[2]; 372a05177dSHoegeun Kwon struct gpio_desc *reset_gpio; 382a05177dSHoegeun Kwon }; 392a05177dSHoegeun Kwon 402a05177dSHoegeun Kwon static const struct drm_display_mode default_mode = { 412a05177dSHoegeun Kwon .clock = 4649, 422a05177dSHoegeun Kwon .hdisplay = 320, 432a05177dSHoegeun Kwon .hsync_start = 320 + 1, 442a05177dSHoegeun Kwon .hsync_end = 320 + 1 + 1, 452a05177dSHoegeun Kwon .htotal = 320 + 1 + 1 + 1, 462a05177dSHoegeun Kwon .vdisplay = 320, 472a05177dSHoegeun Kwon .vsync_start = 320 + 150, 482a05177dSHoegeun Kwon .vsync_end = 320 + 150 + 1, 492a05177dSHoegeun Kwon .vtotal = 320 + 150 + 1 + 2, 502a05177dSHoegeun Kwon .vrefresh = 30, 512a05177dSHoegeun Kwon .flags = 0, 522a05177dSHoegeun Kwon }; 532a05177dSHoegeun Kwon 542a05177dSHoegeun Kwon static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = { 552a05177dSHoegeun Kwon { /* Gamma 10 */ 562a05177dSHoegeun Kwon MCS_MTP_SET3, 572a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26, 582a05177dSHoegeun Kwon 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36, 592a05177dSHoegeun Kwon 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf 602a05177dSHoegeun Kwon }, 612a05177dSHoegeun Kwon { /* gamma 30 */ 622a05177dSHoegeun Kwon MCS_MTP_SET3, 632a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26, 642a05177dSHoegeun Kwon 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34, 652a05177dSHoegeun Kwon 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc 662a05177dSHoegeun Kwon }, 672a05177dSHoegeun Kwon { /* gamma 60 */ 682a05177dSHoegeun Kwon MCS_MTP_SET3, 692a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a, 702a05177dSHoegeun Kwon 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33, 712a05177dSHoegeun Kwon 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5 722a05177dSHoegeun Kwon }, 732a05177dSHoegeun Kwon { /* gamma 90 */ 742a05177dSHoegeun Kwon MCS_MTP_SET3, 752a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29, 762a05177dSHoegeun Kwon 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31, 772a05177dSHoegeun Kwon 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09 782a05177dSHoegeun Kwon }, 792a05177dSHoegeun Kwon { /* gamma 120 */ 802a05177dSHoegeun Kwon MCS_MTP_SET3, 812a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28, 822a05177dSHoegeun Kwon 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31, 832a05177dSHoegeun Kwon 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b 842a05177dSHoegeun Kwon }, 852a05177dSHoegeun Kwon { /* gamma 150 */ 862a05177dSHoegeun Kwon MCS_MTP_SET3, 872a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28, 882a05177dSHoegeun Kwon 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31, 892a05177dSHoegeun Kwon 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29 902a05177dSHoegeun Kwon }, 912a05177dSHoegeun Kwon { /* gamma 200 */ 922a05177dSHoegeun Kwon MCS_MTP_SET3, 932a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29, 942a05177dSHoegeun Kwon 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30, 952a05177dSHoegeun Kwon 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b 962a05177dSHoegeun Kwon }, 972a05177dSHoegeun Kwon { /* gamma 240 */ 982a05177dSHoegeun Kwon MCS_MTP_SET3, 992a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a, 1002a05177dSHoegeun Kwon 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30, 1012a05177dSHoegeun Kwon 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47 1022a05177dSHoegeun Kwon }, 1032a05177dSHoegeun Kwon { /* gamma 300 */ 1042a05177dSHoegeun Kwon MCS_MTP_SET3, 1052a05177dSHoegeun Kwon 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28, 1062a05177dSHoegeun Kwon 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f, 1072a05177dSHoegeun Kwon 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57 1082a05177dSHoegeun Kwon } 1092a05177dSHoegeun Kwon }; 1102a05177dSHoegeun Kwon 1112a05177dSHoegeun Kwon static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel) 1122a05177dSHoegeun Kwon { 1132a05177dSHoegeun Kwon return container_of(panel, struct s6e63j0x03, panel); 1142a05177dSHoegeun Kwon } 1152a05177dSHoegeun Kwon 1162a05177dSHoegeun Kwon static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx, 1172a05177dSHoegeun Kwon const void *seq, size_t len) 1182a05177dSHoegeun Kwon { 1192a05177dSHoegeun Kwon struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 1202a05177dSHoegeun Kwon 1212a05177dSHoegeun Kwon return mipi_dsi_dcs_write_buffer(dsi, seq, len); 1222a05177dSHoegeun Kwon } 1232a05177dSHoegeun Kwon 1242a05177dSHoegeun Kwon #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \ 1252a05177dSHoegeun Kwon ({ \ 1262a05177dSHoegeun Kwon static const u8 d[] = { seq }; \ 1272a05177dSHoegeun Kwon s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \ 1282a05177dSHoegeun Kwon }) 1292a05177dSHoegeun Kwon 1302a05177dSHoegeun Kwon static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx) 1312a05177dSHoegeun Kwon { 1322a05177dSHoegeun Kwon return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a); 1332a05177dSHoegeun Kwon } 1342a05177dSHoegeun Kwon 1352a05177dSHoegeun Kwon static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on) 1362a05177dSHoegeun Kwon { 1372a05177dSHoegeun Kwon if (on) 1382a05177dSHoegeun Kwon return s6e63j0x03_dcs_write_seq_static(ctx, 1392a05177dSHoegeun Kwon MCS_MTP_KEY, 0x5a, 0x5a); 1402a05177dSHoegeun Kwon 1412a05177dSHoegeun Kwon return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5); 1422a05177dSHoegeun Kwon } 1432a05177dSHoegeun Kwon 1442a05177dSHoegeun Kwon static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx) 1452a05177dSHoegeun Kwon { 1462a05177dSHoegeun Kwon int ret; 1472a05177dSHoegeun Kwon 1482a05177dSHoegeun Kwon ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 1492a05177dSHoegeun Kwon if (ret < 0) 1502a05177dSHoegeun Kwon return ret; 1512a05177dSHoegeun Kwon 1522a05177dSHoegeun Kwon msleep(30); 1532a05177dSHoegeun Kwon 1542a05177dSHoegeun Kwon gpiod_set_value(ctx->reset_gpio, 1); 1552a05177dSHoegeun Kwon usleep_range(1000, 2000); 1562a05177dSHoegeun Kwon gpiod_set_value(ctx->reset_gpio, 0); 1572a05177dSHoegeun Kwon usleep_range(5000, 6000); 1582a05177dSHoegeun Kwon 1592a05177dSHoegeun Kwon return 0; 1602a05177dSHoegeun Kwon } 1612a05177dSHoegeun Kwon 1622a05177dSHoegeun Kwon static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx) 1632a05177dSHoegeun Kwon { 1642a05177dSHoegeun Kwon return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 1652a05177dSHoegeun Kwon } 1662a05177dSHoegeun Kwon 1672a05177dSHoegeun Kwon static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness) 1682a05177dSHoegeun Kwon { 1692a05177dSHoegeun Kwon unsigned int index; 1702a05177dSHoegeun Kwon 1712a05177dSHoegeun Kwon index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS); 1722a05177dSHoegeun Kwon 1732a05177dSHoegeun Kwon if (index >= NUM_GAMMA_STEPS) 1742a05177dSHoegeun Kwon index = NUM_GAMMA_STEPS - 1; 1752a05177dSHoegeun Kwon 1762a05177dSHoegeun Kwon return index; 1772a05177dSHoegeun Kwon } 1782a05177dSHoegeun Kwon 1792a05177dSHoegeun Kwon static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx, 1802a05177dSHoegeun Kwon unsigned int brightness) 1812a05177dSHoegeun Kwon { 1822a05177dSHoegeun Kwon struct backlight_device *bl_dev = ctx->bl_dev; 1832a05177dSHoegeun Kwon unsigned int index = s6e63j0x03_get_brightness_index(brightness); 1842a05177dSHoegeun Kwon int ret; 1852a05177dSHoegeun Kwon 1862a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, true); 1872a05177dSHoegeun Kwon if (ret < 0) 1882a05177dSHoegeun Kwon return ret; 1892a05177dSHoegeun Kwon 1902a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT); 1912a05177dSHoegeun Kwon if (ret < 0) 1922a05177dSHoegeun Kwon return ret; 1932a05177dSHoegeun Kwon 1942a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, false); 1952a05177dSHoegeun Kwon if (ret < 0) 1962a05177dSHoegeun Kwon return ret; 1972a05177dSHoegeun Kwon 1982a05177dSHoegeun Kwon bl_dev->props.brightness = brightness; 1992a05177dSHoegeun Kwon 2002a05177dSHoegeun Kwon return 0; 2012a05177dSHoegeun Kwon } 2022a05177dSHoegeun Kwon 2032a05177dSHoegeun Kwon static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev) 2042a05177dSHoegeun Kwon { 2052a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = bl_get_data(bl_dev); 2062a05177dSHoegeun Kwon unsigned int brightness = bl_dev->props.brightness; 2072a05177dSHoegeun Kwon 2082a05177dSHoegeun Kwon return s6e63j0x03_update_gamma(ctx, brightness); 2092a05177dSHoegeun Kwon } 2102a05177dSHoegeun Kwon 2112a05177dSHoegeun Kwon static const struct backlight_ops s6e63j0x03_bl_ops = { 2122a05177dSHoegeun Kwon .update_status = s6e63j0x03_set_brightness, 2132a05177dSHoegeun Kwon }; 2142a05177dSHoegeun Kwon 2152a05177dSHoegeun Kwon static int s6e63j0x03_disable(struct drm_panel *panel) 2162a05177dSHoegeun Kwon { 2172a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 2182a05177dSHoegeun Kwon struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 2192a05177dSHoegeun Kwon int ret; 2202a05177dSHoegeun Kwon 2212a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_display_off(dsi); 2222a05177dSHoegeun Kwon if (ret < 0) 2232a05177dSHoegeun Kwon return ret; 2242a05177dSHoegeun Kwon 2252a05177dSHoegeun Kwon ctx->bl_dev->props.power = FB_BLANK_NORMAL; 2262a05177dSHoegeun Kwon 2272a05177dSHoegeun Kwon ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 2282a05177dSHoegeun Kwon if (ret < 0) 2292a05177dSHoegeun Kwon return ret; 2302a05177dSHoegeun Kwon 2312a05177dSHoegeun Kwon msleep(120); 2322a05177dSHoegeun Kwon 2332a05177dSHoegeun Kwon return 0; 2342a05177dSHoegeun Kwon } 2352a05177dSHoegeun Kwon 2362a05177dSHoegeun Kwon static int s6e63j0x03_unprepare(struct drm_panel *panel) 2372a05177dSHoegeun Kwon { 2382a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 2392a05177dSHoegeun Kwon int ret; 2402a05177dSHoegeun Kwon 2412a05177dSHoegeun Kwon ret = s6e63j0x03_power_off(ctx); 2422a05177dSHoegeun Kwon if (ret < 0) 2432a05177dSHoegeun Kwon return ret; 2442a05177dSHoegeun Kwon 2452a05177dSHoegeun Kwon ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 2462a05177dSHoegeun Kwon 2472a05177dSHoegeun Kwon return 0; 2482a05177dSHoegeun Kwon } 2492a05177dSHoegeun Kwon 2502a05177dSHoegeun Kwon static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx) 2512a05177dSHoegeun Kwon { 2522a05177dSHoegeun Kwon struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 2532a05177dSHoegeun Kwon int ret; 2542a05177dSHoegeun Kwon 2552a05177dSHoegeun Kwon ret = s6e63j0x03_enable_lv2_command(ctx); 2562a05177dSHoegeun Kwon if (ret < 0) 2572a05177dSHoegeun Kwon return ret; 2582a05177dSHoegeun Kwon 2592a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, true); 2602a05177dSHoegeun Kwon if (ret < 0) 2612a05177dSHoegeun Kwon return ret; 2622a05177dSHoegeun Kwon 2632a05177dSHoegeun Kwon /* set porch adjustment */ 2642a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28); 2652a05177dSHoegeun Kwon if (ret < 0) 2662a05177dSHoegeun Kwon return ret; 2672a05177dSHoegeun Kwon 2682a05177dSHoegeun Kwon /* set frame freq */ 2692a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00); 2702a05177dSHoegeun Kwon if (ret < 0) 2712a05177dSHoegeun Kwon return ret; 2722a05177dSHoegeun Kwon 2732a05177dSHoegeun Kwon /* set caset, paset */ 2742a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN, 2752a05177dSHoegeun Kwon default_mode.hdisplay - 1 + FIRST_COLUMN); 2762a05177dSHoegeun Kwon if (ret < 0) 2772a05177dSHoegeun Kwon return ret; 2782a05177dSHoegeun Kwon 2792a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); 2802a05177dSHoegeun Kwon if (ret < 0) 2812a05177dSHoegeun Kwon return ret; 2822a05177dSHoegeun Kwon 2832a05177dSHoegeun Kwon /* set ltps timming 0, 1 */ 2842a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17, 2852a05177dSHoegeun Kwon 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00); 2862a05177dSHoegeun Kwon if (ret < 0) 2872a05177dSHoegeun Kwon return ret; 2882a05177dSHoegeun Kwon 2892a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02); 2902a05177dSHoegeun Kwon if (ret < 0) 2912a05177dSHoegeun Kwon return ret; 2922a05177dSHoegeun Kwon 2932a05177dSHoegeun Kwon /* set param pos te_edge */ 2942a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01); 2952a05177dSHoegeun Kwon if (ret < 0) 2962a05177dSHoegeun Kwon return ret; 2972a05177dSHoegeun Kwon 2982a05177dSHoegeun Kwon /* set te rising edge */ 2992a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f); 3002a05177dSHoegeun Kwon if (ret < 0) 3012a05177dSHoegeun Kwon return ret; 3022a05177dSHoegeun Kwon 3032a05177dSHoegeun Kwon /* set param pos default */ 3042a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00); 3052a05177dSHoegeun Kwon if (ret < 0) 3062a05177dSHoegeun Kwon return ret; 3072a05177dSHoegeun Kwon 3082a05177dSHoegeun Kwon ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 3092a05177dSHoegeun Kwon if (ret < 0) 3102a05177dSHoegeun Kwon return ret; 3112a05177dSHoegeun Kwon 3122a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, false); 3132a05177dSHoegeun Kwon if (ret < 0) 3142a05177dSHoegeun Kwon return ret; 3152a05177dSHoegeun Kwon 3162a05177dSHoegeun Kwon return 0; 3172a05177dSHoegeun Kwon } 3182a05177dSHoegeun Kwon 3192a05177dSHoegeun Kwon static int s6e63j0x03_prepare(struct drm_panel *panel) 3202a05177dSHoegeun Kwon { 3212a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 3222a05177dSHoegeun Kwon int ret; 3232a05177dSHoegeun Kwon 3242a05177dSHoegeun Kwon ret = s6e63j0x03_power_on(ctx); 3252a05177dSHoegeun Kwon if (ret < 0) 3262a05177dSHoegeun Kwon return ret; 3272a05177dSHoegeun Kwon 3282a05177dSHoegeun Kwon ret = s6e63j0x03_panel_init(ctx); 3292a05177dSHoegeun Kwon if (ret < 0) 3302a05177dSHoegeun Kwon goto err; 3312a05177dSHoegeun Kwon 3322a05177dSHoegeun Kwon ctx->bl_dev->props.power = FB_BLANK_NORMAL; 3332a05177dSHoegeun Kwon 3342a05177dSHoegeun Kwon return 0; 3352a05177dSHoegeun Kwon 3362a05177dSHoegeun Kwon err: 3372a05177dSHoegeun Kwon s6e63j0x03_power_off(ctx); 3382a05177dSHoegeun Kwon return ret; 3392a05177dSHoegeun Kwon } 3402a05177dSHoegeun Kwon 3412a05177dSHoegeun Kwon static int s6e63j0x03_enable(struct drm_panel *panel) 3422a05177dSHoegeun Kwon { 3432a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 3442a05177dSHoegeun Kwon struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3452a05177dSHoegeun Kwon int ret; 3462a05177dSHoegeun Kwon 3472a05177dSHoegeun Kwon msleep(120); 3482a05177dSHoegeun Kwon 3492a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, true); 3502a05177dSHoegeun Kwon if (ret < 0) 3512a05177dSHoegeun Kwon return ret; 3522a05177dSHoegeun Kwon 3532a05177dSHoegeun Kwon /* set elvss_cond */ 3542a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09); 3552a05177dSHoegeun Kwon if (ret < 0) 3562a05177dSHoegeun Kwon return ret; 3572a05177dSHoegeun Kwon 3582a05177dSHoegeun Kwon /* set pos */ 3592a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 3602a05177dSHoegeun Kwon MIPI_DCS_SET_ADDRESS_MODE, 0x40); 3612a05177dSHoegeun Kwon if (ret < 0) 3622a05177dSHoegeun Kwon return ret; 3632a05177dSHoegeun Kwon 3642a05177dSHoegeun Kwon /* set default white brightness */ 3652a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); 3662a05177dSHoegeun Kwon if (ret < 0) 3672a05177dSHoegeun Kwon return ret; 3682a05177dSHoegeun Kwon 3692a05177dSHoegeun Kwon /* set white ctrl */ 3702a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 3712a05177dSHoegeun Kwon MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 3722a05177dSHoegeun Kwon if (ret < 0) 3732a05177dSHoegeun Kwon return ret; 3742a05177dSHoegeun Kwon 3752a05177dSHoegeun Kwon /* set acl off */ 3762a05177dSHoegeun Kwon ret = s6e63j0x03_dcs_write_seq_static(ctx, 3772a05177dSHoegeun Kwon MIPI_DCS_WRITE_POWER_SAVE, 0x00); 3782a05177dSHoegeun Kwon if (ret < 0) 3792a05177dSHoegeun Kwon return ret; 3802a05177dSHoegeun Kwon 3812a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 3822a05177dSHoegeun Kwon if (ret < 0) 3832a05177dSHoegeun Kwon return ret; 3842a05177dSHoegeun Kwon 3852a05177dSHoegeun Kwon ret = s6e63j0x03_apply_mtp_key(ctx, false); 3862a05177dSHoegeun Kwon if (ret < 0) 3872a05177dSHoegeun Kwon return ret; 3882a05177dSHoegeun Kwon 3892a05177dSHoegeun Kwon ret = mipi_dsi_dcs_set_display_on(dsi); 3902a05177dSHoegeun Kwon if (ret < 0) 3912a05177dSHoegeun Kwon return ret; 3922a05177dSHoegeun Kwon 3932a05177dSHoegeun Kwon ctx->bl_dev->props.power = FB_BLANK_UNBLANK; 3942a05177dSHoegeun Kwon 3952a05177dSHoegeun Kwon return 0; 3962a05177dSHoegeun Kwon } 3972a05177dSHoegeun Kwon 3982a05177dSHoegeun Kwon static int s6e63j0x03_get_modes(struct drm_panel *panel) 3992a05177dSHoegeun Kwon { 4002a05177dSHoegeun Kwon struct drm_connector *connector = panel->connector; 4012a05177dSHoegeun Kwon struct drm_display_mode *mode; 4022a05177dSHoegeun Kwon 4032a05177dSHoegeun Kwon mode = drm_mode_duplicate(panel->drm, &default_mode); 4042a05177dSHoegeun Kwon if (!mode) { 4052a05177dSHoegeun Kwon DRM_ERROR("failed to add mode %ux%ux@%u\n", 4062a05177dSHoegeun Kwon default_mode.hdisplay, default_mode.vdisplay, 4072a05177dSHoegeun Kwon default_mode.vrefresh); 4082a05177dSHoegeun Kwon return -ENOMEM; 4092a05177dSHoegeun Kwon } 4102a05177dSHoegeun Kwon 4112a05177dSHoegeun Kwon drm_mode_set_name(mode); 4122a05177dSHoegeun Kwon 4132a05177dSHoegeun Kwon mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 4142a05177dSHoegeun Kwon drm_mode_probed_add(connector, mode); 4152a05177dSHoegeun Kwon 4162a05177dSHoegeun Kwon connector->display_info.width_mm = 29; 4172a05177dSHoegeun Kwon connector->display_info.height_mm = 29; 4182a05177dSHoegeun Kwon 4192a05177dSHoegeun Kwon return 1; 4202a05177dSHoegeun Kwon } 4212a05177dSHoegeun Kwon 4222a05177dSHoegeun Kwon static const struct drm_panel_funcs s6e63j0x03_funcs = { 4232a05177dSHoegeun Kwon .disable = s6e63j0x03_disable, 4242a05177dSHoegeun Kwon .unprepare = s6e63j0x03_unprepare, 4252a05177dSHoegeun Kwon .prepare = s6e63j0x03_prepare, 4262a05177dSHoegeun Kwon .enable = s6e63j0x03_enable, 4272a05177dSHoegeun Kwon .get_modes = s6e63j0x03_get_modes, 4282a05177dSHoegeun Kwon }; 4292a05177dSHoegeun Kwon 4302a05177dSHoegeun Kwon static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) 4312a05177dSHoegeun Kwon { 4322a05177dSHoegeun Kwon struct device *dev = &dsi->dev; 4332a05177dSHoegeun Kwon struct s6e63j0x03 *ctx; 4342a05177dSHoegeun Kwon int ret; 4352a05177dSHoegeun Kwon 4362a05177dSHoegeun Kwon ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL); 4372a05177dSHoegeun Kwon if (!ctx) 4382a05177dSHoegeun Kwon return -ENOMEM; 4392a05177dSHoegeun Kwon 4402a05177dSHoegeun Kwon mipi_dsi_set_drvdata(dsi, ctx); 4412a05177dSHoegeun Kwon 4422a05177dSHoegeun Kwon ctx->dev = dev; 4432a05177dSHoegeun Kwon 4442a05177dSHoegeun Kwon dsi->lanes = 1; 4452a05177dSHoegeun Kwon dsi->format = MIPI_DSI_FMT_RGB888; 4462a05177dSHoegeun Kwon dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET; 4472a05177dSHoegeun Kwon 4482a05177dSHoegeun Kwon ctx->supplies[0].supply = "vdd3"; 4492a05177dSHoegeun Kwon ctx->supplies[1].supply = "vci"; 4502a05177dSHoegeun Kwon ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 4512a05177dSHoegeun Kwon ctx->supplies); 4522a05177dSHoegeun Kwon if (ret < 0) { 4532a05177dSHoegeun Kwon dev_err(dev, "failed to get regulators: %d\n", ret); 4542a05177dSHoegeun Kwon return ret; 4552a05177dSHoegeun Kwon } 4562a05177dSHoegeun Kwon 4572a05177dSHoegeun Kwon ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 4582a05177dSHoegeun Kwon if (IS_ERR(ctx->reset_gpio)) { 4592a05177dSHoegeun Kwon dev_err(dev, "cannot get reset-gpio: %ld\n", 4602a05177dSHoegeun Kwon PTR_ERR(ctx->reset_gpio)); 4612a05177dSHoegeun Kwon return PTR_ERR(ctx->reset_gpio); 4622a05177dSHoegeun Kwon } 4632a05177dSHoegeun Kwon 4642a05177dSHoegeun Kwon drm_panel_init(&ctx->panel); 4652a05177dSHoegeun Kwon ctx->panel.dev = dev; 4662a05177dSHoegeun Kwon ctx->panel.funcs = &s6e63j0x03_funcs; 4672a05177dSHoegeun Kwon 4682a05177dSHoegeun Kwon ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, 4692a05177dSHoegeun Kwon &s6e63j0x03_bl_ops, NULL); 4702a05177dSHoegeun Kwon if (IS_ERR(ctx->bl_dev)) { 4712a05177dSHoegeun Kwon dev_err(dev, "failed to register backlight device\n"); 4722a05177dSHoegeun Kwon return PTR_ERR(ctx->bl_dev); 4732a05177dSHoegeun Kwon } 4742a05177dSHoegeun Kwon 4752a05177dSHoegeun Kwon ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS; 4762a05177dSHoegeun Kwon ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS; 4772a05177dSHoegeun Kwon ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 4782a05177dSHoegeun Kwon 4792a05177dSHoegeun Kwon ret = drm_panel_add(&ctx->panel); 4802a05177dSHoegeun Kwon if (ret < 0) 4812a05177dSHoegeun Kwon goto unregister_backlight; 4822a05177dSHoegeun Kwon 4832a05177dSHoegeun Kwon ret = mipi_dsi_attach(dsi); 4842a05177dSHoegeun Kwon if (ret < 0) 4852a05177dSHoegeun Kwon goto remove_panel; 4862a05177dSHoegeun Kwon 4872a05177dSHoegeun Kwon return ret; 4882a05177dSHoegeun Kwon 4892a05177dSHoegeun Kwon remove_panel: 4902a05177dSHoegeun Kwon drm_panel_remove(&ctx->panel); 4912a05177dSHoegeun Kwon 4922a05177dSHoegeun Kwon unregister_backlight: 4932a05177dSHoegeun Kwon backlight_device_unregister(ctx->bl_dev); 4942a05177dSHoegeun Kwon 4952a05177dSHoegeun Kwon return ret; 4962a05177dSHoegeun Kwon } 4972a05177dSHoegeun Kwon 4982a05177dSHoegeun Kwon static int s6e63j0x03_remove(struct mipi_dsi_device *dsi) 4992a05177dSHoegeun Kwon { 5002a05177dSHoegeun Kwon struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi); 5012a05177dSHoegeun Kwon 5022a05177dSHoegeun Kwon mipi_dsi_detach(dsi); 5032a05177dSHoegeun Kwon drm_panel_remove(&ctx->panel); 5042a05177dSHoegeun Kwon 5052a05177dSHoegeun Kwon backlight_device_unregister(ctx->bl_dev); 5062a05177dSHoegeun Kwon 5072a05177dSHoegeun Kwon return 0; 5082a05177dSHoegeun Kwon } 5092a05177dSHoegeun Kwon 5102a05177dSHoegeun Kwon static const struct of_device_id s6e63j0x03_of_match[] = { 5112a05177dSHoegeun Kwon { .compatible = "samsung,s6e63j0x03" }, 5122a05177dSHoegeun Kwon { } 5132a05177dSHoegeun Kwon }; 5142a05177dSHoegeun Kwon MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match); 5152a05177dSHoegeun Kwon 5162a05177dSHoegeun Kwon static struct mipi_dsi_driver s6e63j0x03_driver = { 5172a05177dSHoegeun Kwon .probe = s6e63j0x03_probe, 5182a05177dSHoegeun Kwon .remove = s6e63j0x03_remove, 5192a05177dSHoegeun Kwon .driver = { 5202a05177dSHoegeun Kwon .name = "panel_samsung_s6e63j0x03", 5212a05177dSHoegeun Kwon .of_match_table = s6e63j0x03_of_match, 5222a05177dSHoegeun Kwon }, 5232a05177dSHoegeun Kwon }; 5242a05177dSHoegeun Kwon module_mipi_dsi_driver(s6e63j0x03_driver); 5252a05177dSHoegeun Kwon 5262a05177dSHoegeun Kwon MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 5272a05177dSHoegeun Kwon MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>"); 5282a05177dSHoegeun Kwon MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver"); 5292a05177dSHoegeun Kwon MODULE_LICENSE("GPL v2"); 530