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