162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Inki Dae <inki.dae@samsung.com> 862306a36Sopenharmony_ci * Hoegeun Kwon <hoegeun.kwon@samsung.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/backlight.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <video/mipi_display.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 2062306a36Sopenharmony_ci#include <drm/drm_modes.h> 2162306a36Sopenharmony_ci#include <drm/drm_panel.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MCS_LEVEL2_KEY 0xf0 2462306a36Sopenharmony_ci#define MCS_MTP_KEY 0xf1 2562306a36Sopenharmony_ci#define MCS_MTP_SET3 0xd4 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define MAX_BRIGHTNESS 100 2862306a36Sopenharmony_ci#define DEFAULT_BRIGHTNESS 80 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define NUM_GAMMA_STEPS 9 3162306a36Sopenharmony_ci#define GAMMA_CMD_CNT 28 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define FIRST_COLUMN 20 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct s6e63j0x03 { 3662306a36Sopenharmony_ci struct device *dev; 3762306a36Sopenharmony_ci struct drm_panel panel; 3862306a36Sopenharmony_ci struct backlight_device *bl_dev; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci struct regulator_bulk_data supplies[2]; 4162306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const struct drm_display_mode default_mode = { 4562306a36Sopenharmony_ci .clock = 4649, 4662306a36Sopenharmony_ci .hdisplay = 320, 4762306a36Sopenharmony_ci .hsync_start = 320 + 1, 4862306a36Sopenharmony_ci .hsync_end = 320 + 1 + 1, 4962306a36Sopenharmony_ci .htotal = 320 + 1 + 1 + 1, 5062306a36Sopenharmony_ci .vdisplay = 320, 5162306a36Sopenharmony_ci .vsync_start = 320 + 150, 5262306a36Sopenharmony_ci .vsync_end = 320 + 150 + 1, 5362306a36Sopenharmony_ci .vtotal = 320 + 150 + 1 + 2, 5462306a36Sopenharmony_ci .flags = 0, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = { 5862306a36Sopenharmony_ci { /* Gamma 10 */ 5962306a36Sopenharmony_ci MCS_MTP_SET3, 6062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26, 6162306a36Sopenharmony_ci 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36, 6262306a36Sopenharmony_ci 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf 6362306a36Sopenharmony_ci }, 6462306a36Sopenharmony_ci { /* gamma 30 */ 6562306a36Sopenharmony_ci MCS_MTP_SET3, 6662306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26, 6762306a36Sopenharmony_ci 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34, 6862306a36Sopenharmony_ci 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci { /* gamma 60 */ 7162306a36Sopenharmony_ci MCS_MTP_SET3, 7262306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a, 7362306a36Sopenharmony_ci 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33, 7462306a36Sopenharmony_ci 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5 7562306a36Sopenharmony_ci }, 7662306a36Sopenharmony_ci { /* gamma 90 */ 7762306a36Sopenharmony_ci MCS_MTP_SET3, 7862306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29, 7962306a36Sopenharmony_ci 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31, 8062306a36Sopenharmony_ci 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09 8162306a36Sopenharmony_ci }, 8262306a36Sopenharmony_ci { /* gamma 120 */ 8362306a36Sopenharmony_ci MCS_MTP_SET3, 8462306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28, 8562306a36Sopenharmony_ci 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31, 8662306a36Sopenharmony_ci 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci { /* gamma 150 */ 8962306a36Sopenharmony_ci MCS_MTP_SET3, 9062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28, 9162306a36Sopenharmony_ci 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31, 9262306a36Sopenharmony_ci 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29 9362306a36Sopenharmony_ci }, 9462306a36Sopenharmony_ci { /* gamma 200 */ 9562306a36Sopenharmony_ci MCS_MTP_SET3, 9662306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29, 9762306a36Sopenharmony_ci 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30, 9862306a36Sopenharmony_ci 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci { /* gamma 240 */ 10162306a36Sopenharmony_ci MCS_MTP_SET3, 10262306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a, 10362306a36Sopenharmony_ci 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30, 10462306a36Sopenharmony_ci 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47 10562306a36Sopenharmony_ci }, 10662306a36Sopenharmony_ci { /* gamma 300 */ 10762306a36Sopenharmony_ci MCS_MTP_SET3, 10862306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28, 10962306a36Sopenharmony_ci 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f, 11062306a36Sopenharmony_ci 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return container_of(panel, struct s6e63j0x03, panel); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx, 12062306a36Sopenharmony_ci const void *seq, size_t len) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return mipi_dsi_dcs_write_buffer(dsi, seq, len); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \ 12862306a36Sopenharmony_ci ({ \ 12962306a36Sopenharmony_ci static const u8 d[] = { seq }; \ 13062306a36Sopenharmony_ci s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \ 13162306a36Sopenharmony_ci }) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (on) 14162306a36Sopenharmony_ci return s6e63j0x03_dcs_write_seq_static(ctx, 14262306a36Sopenharmony_ci MCS_MTP_KEY, 0x5a, 0x5a); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int s6e63j0x03_power_on(struct s6e63j0x03 *ctx) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 15262306a36Sopenharmony_ci if (ret < 0) 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci msleep(30); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci gpiod_set_value(ctx->reset_gpio, 1); 15862306a36Sopenharmony_ci usleep_range(1000, 2000); 15962306a36Sopenharmony_ci gpiod_set_value(ctx->reset_gpio, 0); 16062306a36Sopenharmony_ci usleep_range(5000, 6000); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int s6e63j0x03_power_off(struct s6e63j0x03 *ctx) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci unsigned int index; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (index >= NUM_GAMMA_STEPS) 17762306a36Sopenharmony_ci index = NUM_GAMMA_STEPS - 1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return index; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx, 18362306a36Sopenharmony_ci unsigned int brightness) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct backlight_device *bl_dev = ctx->bl_dev; 18662306a36Sopenharmony_ci unsigned int index = s6e63j0x03_get_brightness_index(brightness); 18762306a36Sopenharmony_ci int ret; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, true); 19062306a36Sopenharmony_ci if (ret < 0) 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT); 19462306a36Sopenharmony_ci if (ret < 0) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, false); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci bl_dev->props.brightness = brightness; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int s6e63j0x03_set_brightness(struct backlight_device *bl_dev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct s6e63j0x03 *ctx = bl_get_data(bl_dev); 20962306a36Sopenharmony_ci unsigned int brightness = bl_dev->props.brightness; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return s6e63j0x03_update_gamma(ctx, brightness); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct backlight_ops s6e63j0x03_bl_ops = { 21562306a36Sopenharmony_ci .update_status = s6e63j0x03_set_brightness, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int s6e63j0x03_disable(struct drm_panel *panel) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 22162306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 22562306a36Sopenharmony_ci if (ret < 0) 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_NORMAL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 23162306a36Sopenharmony_ci if (ret < 0) 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci msleep(120); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int s6e63j0x03_unprepare(struct drm_panel *panel) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ret = s6e63j0x03_power_off(ctx); 24562306a36Sopenharmony_ci if (ret < 0) 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 25662306a36Sopenharmony_ci int ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = s6e63j0x03_enable_lv2_command(ctx); 25962306a36Sopenharmony_ci if (ret < 0) 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, true); 26362306a36Sopenharmony_ci if (ret < 0) 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* set porch adjustment */ 26762306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28); 26862306a36Sopenharmony_ci if (ret < 0) 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* set frame freq */ 27262306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00); 27362306a36Sopenharmony_ci if (ret < 0) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* set caset, paset */ 27762306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN, 27862306a36Sopenharmony_ci default_mode.hdisplay - 1 + FIRST_COLUMN); 27962306a36Sopenharmony_ci if (ret < 0) 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); 28362306a36Sopenharmony_ci if (ret < 0) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* set ltps timming 0, 1 */ 28762306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17, 28862306a36Sopenharmony_ci 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00); 28962306a36Sopenharmony_ci if (ret < 0) 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02); 29362306a36Sopenharmony_ci if (ret < 0) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* set param pos te_edge */ 29762306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01); 29862306a36Sopenharmony_ci if (ret < 0) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* set te rising edge */ 30262306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f); 30362306a36Sopenharmony_ci if (ret < 0) 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* set param pos default */ 30762306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00); 30862306a36Sopenharmony_ci if (ret < 0) 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, false); 31662306a36Sopenharmony_ci if (ret < 0) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int s6e63j0x03_prepare(struct drm_panel *panel) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = s6e63j0x03_power_on(ctx); 32862306a36Sopenharmony_ci if (ret < 0) 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ret = s6e63j0x03_panel_init(ctx); 33262306a36Sopenharmony_ci if (ret < 0) 33362306a36Sopenharmony_ci goto err; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_NORMAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cierr: 34062306a36Sopenharmony_ci s6e63j0x03_power_off(ctx); 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int s6e63j0x03_enable(struct drm_panel *panel) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 34762306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci msleep(120); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, true); 35362306a36Sopenharmony_ci if (ret < 0) 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* set elvss_cond */ 35762306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09); 35862306a36Sopenharmony_ci if (ret < 0) 35962306a36Sopenharmony_ci return ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* set pos */ 36262306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 36362306a36Sopenharmony_ci MIPI_DCS_SET_ADDRESS_MODE, 0x40); 36462306a36Sopenharmony_ci if (ret < 0) 36562306a36Sopenharmony_ci return ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* set default white brightness */ 36862306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); 36962306a36Sopenharmony_ci if (ret < 0) 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* set white ctrl */ 37362306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 37462306a36Sopenharmony_ci MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 37562306a36Sopenharmony_ci if (ret < 0) 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* set acl off */ 37962306a36Sopenharmony_ci ret = s6e63j0x03_dcs_write_seq_static(ctx, 38062306a36Sopenharmony_ci MIPI_DCS_WRITE_POWER_SAVE, 0x00); 38162306a36Sopenharmony_ci if (ret < 0) 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 38562306a36Sopenharmony_ci if (ret < 0) 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = s6e63j0x03_apply_mtp_key(ctx, false); 38962306a36Sopenharmony_ci if (ret < 0) 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_UNBLANK; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int s6e63j0x03_get_modes(struct drm_panel *panel, 40262306a36Sopenharmony_ci struct drm_connector *connector) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct drm_display_mode *mode; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 40762306a36Sopenharmony_ci if (!mode) { 40862306a36Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 40962306a36Sopenharmony_ci default_mode.hdisplay, default_mode.vdisplay, 41062306a36Sopenharmony_ci drm_mode_vrefresh(&default_mode)); 41162306a36Sopenharmony_ci return -ENOMEM; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci drm_mode_set_name(mode); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 41762306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci connector->display_info.width_mm = 29; 42062306a36Sopenharmony_ci connector->display_info.height_mm = 29; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 1; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct drm_panel_funcs s6e63j0x03_funcs = { 42662306a36Sopenharmony_ci .disable = s6e63j0x03_disable, 42762306a36Sopenharmony_ci .unprepare = s6e63j0x03_unprepare, 42862306a36Sopenharmony_ci .prepare = s6e63j0x03_prepare, 42962306a36Sopenharmony_ci .enable = s6e63j0x03_enable, 43062306a36Sopenharmony_ci .get_modes = s6e63j0x03_get_modes, 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int s6e63j0x03_probe(struct mipi_dsi_device *dsi) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct device *dev = &dsi->dev; 43662306a36Sopenharmony_ci struct s6e63j0x03 *ctx; 43762306a36Sopenharmony_ci int ret; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL); 44062306a36Sopenharmony_ci if (!ctx) 44162306a36Sopenharmony_ci return -ENOMEM; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ctx->dev = dev; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci dsi->lanes = 1; 44862306a36Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 44962306a36Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO_NO_HFP | 45062306a36Sopenharmony_ci MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ctx->supplies[0].supply = "vdd3"; 45362306a36Sopenharmony_ci ctx->supplies[1].supply = "vci"; 45462306a36Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 45562306a36Sopenharmony_ci ctx->supplies); 45662306a36Sopenharmony_ci if (ret < 0) 45762306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to get regulators\n"); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 46062306a36Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) 46162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 46262306a36Sopenharmony_ci "cannot get reset-gpio\n"); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs, 46562306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 46662306a36Sopenharmony_ci ctx->panel.prepare_prev_first = true; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, 46962306a36Sopenharmony_ci &s6e63j0x03_bl_ops, NULL); 47062306a36Sopenharmony_ci if (IS_ERR(ctx->bl_dev)) 47162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctx->bl_dev), 47262306a36Sopenharmony_ci "failed to register backlight device\n"); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS; 47562306a36Sopenharmony_ci ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS; 47662306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci drm_panel_add(&ctx->panel); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ret = mipi_dsi_attach(dsi); 48162306a36Sopenharmony_ci if (ret < 0) 48262306a36Sopenharmony_ci goto remove_panel; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ciremove_panel: 48762306a36Sopenharmony_ci drm_panel_remove(&ctx->panel); 48862306a36Sopenharmony_ci backlight_device_unregister(ctx->bl_dev); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void s6e63j0x03_remove(struct mipi_dsi_device *dsi) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci mipi_dsi_detach(dsi); 49862306a36Sopenharmony_ci drm_panel_remove(&ctx->panel); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci backlight_device_unregister(ctx->bl_dev); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic const struct of_device_id s6e63j0x03_of_match[] = { 50462306a36Sopenharmony_ci { .compatible = "samsung,s6e63j0x03" }, 50562306a36Sopenharmony_ci { } 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s6e63j0x03_of_match); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic struct mipi_dsi_driver s6e63j0x03_driver = { 51062306a36Sopenharmony_ci .probe = s6e63j0x03_probe, 51162306a36Sopenharmony_ci .remove = s6e63j0x03_remove, 51262306a36Sopenharmony_ci .driver = { 51362306a36Sopenharmony_ci .name = "panel_samsung_s6e63j0x03", 51462306a36Sopenharmony_ci .of_match_table = s6e63j0x03_of_match, 51562306a36Sopenharmony_ci }, 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_cimodule_mipi_dsi_driver(s6e63j0x03_driver); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 52062306a36Sopenharmony_ciMODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>"); 52162306a36Sopenharmony_ciMODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver"); 52262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 523