162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/backlight.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_graph.h>
1262306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
1362306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <video/mipi_display.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
1862306a36Sopenharmony_ci#include <drm/drm_modes.h>
1962306a36Sopenharmony_ci#include <drm/drm_panel.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const char * const regulator_names[] = {
2262306a36Sopenharmony_ci	"vdda",
2362306a36Sopenharmony_ci	"vdispp",
2462306a36Sopenharmony_ci	"vdispn",
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic unsigned long const regulator_enable_loads[] = {
2862306a36Sopenharmony_ci	62000,
2962306a36Sopenharmony_ci	100000,
3062306a36Sopenharmony_ci	100000,
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic unsigned long const regulator_disable_loads[] = {
3462306a36Sopenharmony_ci	80,
3562306a36Sopenharmony_ci	100,
3662306a36Sopenharmony_ci	100,
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct cmd_set {
4062306a36Sopenharmony_ci	u8 commands[4];
4162306a36Sopenharmony_ci	u8 size;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct nt35597_config {
4562306a36Sopenharmony_ci	u32 width_mm;
4662306a36Sopenharmony_ci	u32 height_mm;
4762306a36Sopenharmony_ci	const char *panel_name;
4862306a36Sopenharmony_ci	const struct cmd_set *panel_on_cmds;
4962306a36Sopenharmony_ci	u32 num_on_cmds;
5062306a36Sopenharmony_ci	const struct drm_display_mode *dm;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct truly_nt35597 {
5462306a36Sopenharmony_ci	struct device *dev;
5562306a36Sopenharmony_ci	struct drm_panel panel;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
6062306a36Sopenharmony_ci	struct gpio_desc *mode_gpio;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	struct backlight_device *backlight;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	struct mipi_dsi_device *dsi[2];
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	const struct nt35597_config *config;
6762306a36Sopenharmony_ci	bool prepared;
6862306a36Sopenharmony_ci	bool enabled;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return container_of(panel, struct truly_nt35597, panel);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct cmd_set qcom_2k_panel_magic_cmds[] = {
7762306a36Sopenharmony_ci	/* CMD2_P0 */
7862306a36Sopenharmony_ci	{ { 0xff, 0x20 }, 2 },
7962306a36Sopenharmony_ci	{ { 0xfb, 0x01 }, 2 },
8062306a36Sopenharmony_ci	{ { 0x00, 0x01 }, 2 },
8162306a36Sopenharmony_ci	{ { 0x01, 0x55 }, 2 },
8262306a36Sopenharmony_ci	{ { 0x02, 0x45 }, 2 },
8362306a36Sopenharmony_ci	{ { 0x05, 0x40 }, 2 },
8462306a36Sopenharmony_ci	{ { 0x06, 0x19 }, 2 },
8562306a36Sopenharmony_ci	{ { 0x07, 0x1e }, 2 },
8662306a36Sopenharmony_ci	{ { 0x0b, 0x73 }, 2 },
8762306a36Sopenharmony_ci	{ { 0x0c, 0x73 }, 2 },
8862306a36Sopenharmony_ci	{ { 0x0e, 0xb0 }, 2 },
8962306a36Sopenharmony_ci	{ { 0x0f, 0xae }, 2 },
9062306a36Sopenharmony_ci	{ { 0x11, 0xb8 }, 2 },
9162306a36Sopenharmony_ci	{ { 0x13, 0x00 }, 2 },
9262306a36Sopenharmony_ci	{ { 0x58, 0x80 }, 2 },
9362306a36Sopenharmony_ci	{ { 0x59, 0x01 }, 2 },
9462306a36Sopenharmony_ci	{ { 0x5a, 0x00 }, 2 },
9562306a36Sopenharmony_ci	{ { 0x5b, 0x01 }, 2 },
9662306a36Sopenharmony_ci	{ { 0x5c, 0x80 }, 2 },
9762306a36Sopenharmony_ci	{ { 0x5d, 0x81 }, 2 },
9862306a36Sopenharmony_ci	{ { 0x5e, 0x00 }, 2 },
9962306a36Sopenharmony_ci	{ { 0x5f, 0x01 }, 2 },
10062306a36Sopenharmony_ci	{ { 0x72, 0x11 }, 2 },
10162306a36Sopenharmony_ci	{ { 0x68, 0x03 }, 2 },
10262306a36Sopenharmony_ci	/* CMD2_P4 */
10362306a36Sopenharmony_ci	{ { 0xFF, 0x24 }, 2 },
10462306a36Sopenharmony_ci	{ { 0xFB, 0x01 }, 2 },
10562306a36Sopenharmony_ci	{ { 0x00, 0x1C }, 2 },
10662306a36Sopenharmony_ci	{ { 0x01, 0x0B }, 2 },
10762306a36Sopenharmony_ci	{ { 0x02, 0x0C }, 2 },
10862306a36Sopenharmony_ci	{ { 0x03, 0x01 }, 2 },
10962306a36Sopenharmony_ci	{ { 0x04, 0x0F }, 2 },
11062306a36Sopenharmony_ci	{ { 0x05, 0x10 }, 2 },
11162306a36Sopenharmony_ci	{ { 0x06, 0x10 }, 2 },
11262306a36Sopenharmony_ci	{ { 0x07, 0x10 }, 2 },
11362306a36Sopenharmony_ci	{ { 0x08, 0x89 }, 2 },
11462306a36Sopenharmony_ci	{ { 0x09, 0x8A }, 2 },
11562306a36Sopenharmony_ci	{ { 0x0A, 0x13 }, 2 },
11662306a36Sopenharmony_ci	{ { 0x0B, 0x13 }, 2 },
11762306a36Sopenharmony_ci	{ { 0x0C, 0x15 }, 2 },
11862306a36Sopenharmony_ci	{ { 0x0D, 0x15 }, 2 },
11962306a36Sopenharmony_ci	{ { 0x0E, 0x17 }, 2 },
12062306a36Sopenharmony_ci	{ { 0x0F, 0x17 }, 2 },
12162306a36Sopenharmony_ci	{ { 0x10, 0x1C }, 2 },
12262306a36Sopenharmony_ci	{ { 0x11, 0x0B }, 2 },
12362306a36Sopenharmony_ci	{ { 0x12, 0x0C }, 2 },
12462306a36Sopenharmony_ci	{ { 0x13, 0x01 }, 2 },
12562306a36Sopenharmony_ci	{ { 0x14, 0x0F }, 2 },
12662306a36Sopenharmony_ci	{ { 0x15, 0x10 }, 2 },
12762306a36Sopenharmony_ci	{ { 0x16, 0x10 }, 2 },
12862306a36Sopenharmony_ci	{ { 0x17, 0x10 }, 2 },
12962306a36Sopenharmony_ci	{ { 0x18, 0x89 }, 2 },
13062306a36Sopenharmony_ci	{ { 0x19, 0x8A }, 2 },
13162306a36Sopenharmony_ci	{ { 0x1A, 0x13 }, 2 },
13262306a36Sopenharmony_ci	{ { 0x1B, 0x13 }, 2 },
13362306a36Sopenharmony_ci	{ { 0x1C, 0x15 }, 2 },
13462306a36Sopenharmony_ci	{ { 0x1D, 0x15 }, 2 },
13562306a36Sopenharmony_ci	{ { 0x1E, 0x17 }, 2 },
13662306a36Sopenharmony_ci	{ { 0x1F, 0x17 }, 2 },
13762306a36Sopenharmony_ci	/* STV */
13862306a36Sopenharmony_ci	{ { 0x20, 0x40 }, 2 },
13962306a36Sopenharmony_ci	{ { 0x21, 0x01 }, 2 },
14062306a36Sopenharmony_ci	{ { 0x22, 0x00 }, 2 },
14162306a36Sopenharmony_ci	{ { 0x23, 0x40 }, 2 },
14262306a36Sopenharmony_ci	{ { 0x24, 0x40 }, 2 },
14362306a36Sopenharmony_ci	{ { 0x25, 0x6D }, 2 },
14462306a36Sopenharmony_ci	{ { 0x26, 0x40 }, 2 },
14562306a36Sopenharmony_ci	{ { 0x27, 0x40 }, 2 },
14662306a36Sopenharmony_ci	/* Vend */
14762306a36Sopenharmony_ci	{ { 0xE0, 0x00 }, 2 },
14862306a36Sopenharmony_ci	{ { 0xDC, 0x21 }, 2 },
14962306a36Sopenharmony_ci	{ { 0xDD, 0x22 }, 2 },
15062306a36Sopenharmony_ci	{ { 0xDE, 0x07 }, 2 },
15162306a36Sopenharmony_ci	{ { 0xDF, 0x07 }, 2 },
15262306a36Sopenharmony_ci	{ { 0xE3, 0x6D }, 2 },
15362306a36Sopenharmony_ci	{ { 0xE1, 0x07 }, 2 },
15462306a36Sopenharmony_ci	{ { 0xE2, 0x07 }, 2 },
15562306a36Sopenharmony_ci	/* UD */
15662306a36Sopenharmony_ci	{ { 0x29, 0xD8 }, 2 },
15762306a36Sopenharmony_ci	{ { 0x2A, 0x2A }, 2 },
15862306a36Sopenharmony_ci	/* CLK */
15962306a36Sopenharmony_ci	{ { 0x4B, 0x03 }, 2 },
16062306a36Sopenharmony_ci	{ { 0x4C, 0x11 }, 2 },
16162306a36Sopenharmony_ci	{ { 0x4D, 0x10 }, 2 },
16262306a36Sopenharmony_ci	{ { 0x4E, 0x01 }, 2 },
16362306a36Sopenharmony_ci	{ { 0x4F, 0x01 }, 2 },
16462306a36Sopenharmony_ci	{ { 0x50, 0x10 }, 2 },
16562306a36Sopenharmony_ci	{ { 0x51, 0x00 }, 2 },
16662306a36Sopenharmony_ci	{ { 0x52, 0x80 }, 2 },
16762306a36Sopenharmony_ci	{ { 0x53, 0x00 }, 2 },
16862306a36Sopenharmony_ci	{ { 0x56, 0x00 }, 2 },
16962306a36Sopenharmony_ci	{ { 0x54, 0x07 }, 2 },
17062306a36Sopenharmony_ci	{ { 0x58, 0x07 }, 2 },
17162306a36Sopenharmony_ci	{ { 0x55, 0x25 }, 2 },
17262306a36Sopenharmony_ci	/* Reset XDONB */
17362306a36Sopenharmony_ci	{ { 0x5B, 0x43 }, 2 },
17462306a36Sopenharmony_ci	{ { 0x5C, 0x00 }, 2 },
17562306a36Sopenharmony_ci	{ { 0x5F, 0x73 }, 2 },
17662306a36Sopenharmony_ci	{ { 0x60, 0x73 }, 2 },
17762306a36Sopenharmony_ci	{ { 0x63, 0x22 }, 2 },
17862306a36Sopenharmony_ci	{ { 0x64, 0x00 }, 2 },
17962306a36Sopenharmony_ci	{ { 0x67, 0x08 }, 2 },
18062306a36Sopenharmony_ci	{ { 0x68, 0x04 }, 2 },
18162306a36Sopenharmony_ci	/* Resolution:1440x2560 */
18262306a36Sopenharmony_ci	{ { 0x72, 0x02 }, 2 },
18362306a36Sopenharmony_ci	/* mux */
18462306a36Sopenharmony_ci	{ { 0x7A, 0x80 }, 2 },
18562306a36Sopenharmony_ci	{ { 0x7B, 0x91 }, 2 },
18662306a36Sopenharmony_ci	{ { 0x7C, 0xD8 }, 2 },
18762306a36Sopenharmony_ci	{ { 0x7D, 0x60 }, 2 },
18862306a36Sopenharmony_ci	{ { 0x7F, 0x15 }, 2 },
18962306a36Sopenharmony_ci	{ { 0x75, 0x15 }, 2 },
19062306a36Sopenharmony_ci	/* ABOFF */
19162306a36Sopenharmony_ci	{ { 0xB3, 0xC0 }, 2 },
19262306a36Sopenharmony_ci	{ { 0xB4, 0x00 }, 2 },
19362306a36Sopenharmony_ci	{ { 0xB5, 0x00 }, 2 },
19462306a36Sopenharmony_ci	/* Source EQ */
19562306a36Sopenharmony_ci	{ { 0x78, 0x00 }, 2 },
19662306a36Sopenharmony_ci	{ { 0x79, 0x00 }, 2 },
19762306a36Sopenharmony_ci	{ { 0x80, 0x00 }, 2 },
19862306a36Sopenharmony_ci	{ { 0x83, 0x00 }, 2 },
19962306a36Sopenharmony_ci	/* FP BP */
20062306a36Sopenharmony_ci	{ { 0x93, 0x0A }, 2 },
20162306a36Sopenharmony_ci	{ { 0x94, 0x0A }, 2 },
20262306a36Sopenharmony_ci	/* Inversion Type */
20362306a36Sopenharmony_ci	{ { 0x8A, 0x00 }, 2 },
20462306a36Sopenharmony_ci	{ { 0x9B, 0xFF }, 2 },
20562306a36Sopenharmony_ci	/* IMGSWAP =1 @PortSwap=1 */
20662306a36Sopenharmony_ci	{ { 0x9D, 0xB0 }, 2 },
20762306a36Sopenharmony_ci	{ { 0x9F, 0x63 }, 2 },
20862306a36Sopenharmony_ci	{ { 0x98, 0x10 }, 2 },
20962306a36Sopenharmony_ci	/* FRM */
21062306a36Sopenharmony_ci	{ { 0xEC, 0x00 }, 2 },
21162306a36Sopenharmony_ci	/* CMD1 */
21262306a36Sopenharmony_ci	{ { 0xFF, 0x10 }, 2 },
21362306a36Sopenharmony_ci	/* VBP+VSA=,VFP = 10H */
21462306a36Sopenharmony_ci	{ { 0x3B, 0x03, 0x0A, 0x0A }, 4 },
21562306a36Sopenharmony_ci	/* FTE on */
21662306a36Sopenharmony_ci	{ { 0x35, 0x00 }, 2 },
21762306a36Sopenharmony_ci	/* EN_BK =1(auto black) */
21862306a36Sopenharmony_ci	{ { 0xE5, 0x01 }, 2 },
21962306a36Sopenharmony_ci	/* CMD mode(10) VDO mode(03) */
22062306a36Sopenharmony_ci	{ { 0xBB, 0x03 }, 2 },
22162306a36Sopenharmony_ci	/* Non Reload MTP */
22262306a36Sopenharmony_ci	{ { 0xFB, 0x01 }, 2 },
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int truly_dcs_write(struct drm_panel *panel, u32 command)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
22862306a36Sopenharmony_ci	int i, ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
23162306a36Sopenharmony_ci		ret = mipi_dsi_dcs_write(ctx->dsi[i], command, NULL, 0);
23262306a36Sopenharmony_ci		if (ret < 0) {
23362306a36Sopenharmony_ci			dev_err(ctx->dev, "cmd 0x%x failed for dsi = %d\n", command, i);
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return ret;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int truly_dcs_write_buf(struct drm_panel *panel,
24162306a36Sopenharmony_ci	u32 size, const u8 *buf)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
24462306a36Sopenharmony_ci	int ret = 0;
24562306a36Sopenharmony_ci	int i;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
24862306a36Sopenharmony_ci		ret = mipi_dsi_dcs_write_buffer(ctx->dsi[i], buf, size);
24962306a36Sopenharmony_ci		if (ret < 0) {
25062306a36Sopenharmony_ci			dev_err(ctx->dev, "failed to tx cmd [%d], err: %d\n", i, ret);
25162306a36Sopenharmony_ci			return ret;
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int truly_35597_power_on(struct truly_nt35597 *ctx)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	int ret, i;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
26362306a36Sopenharmony_ci		ret = regulator_set_load(ctx->supplies[i].consumer,
26462306a36Sopenharmony_ci					regulator_enable_loads[i]);
26562306a36Sopenharmony_ci		if (ret)
26662306a36Sopenharmony_ci			return ret;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
27062306a36Sopenharmony_ci	if (ret < 0)
27162306a36Sopenharmony_ci		return ret;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * Reset sequence of truly panel requires the panel to be
27562306a36Sopenharmony_ci	 * out of reset for 10ms, followed by being held in reset
27662306a36Sopenharmony_ci	 * for 10ms and then out again
27762306a36Sopenharmony_ci	 */
27862306a36Sopenharmony_ci	gpiod_set_value(ctx->reset_gpio, 0);
27962306a36Sopenharmony_ci	usleep_range(10000, 20000);
28062306a36Sopenharmony_ci	gpiod_set_value(ctx->reset_gpio, 1);
28162306a36Sopenharmony_ci	usleep_range(10000, 20000);
28262306a36Sopenharmony_ci	gpiod_set_value(ctx->reset_gpio, 0);
28362306a36Sopenharmony_ci	usleep_range(10000, 20000);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int truly_nt35597_power_off(struct truly_nt35597 *ctx)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int ret = 0;
29162306a36Sopenharmony_ci	int i;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	gpiod_set_value(ctx->reset_gpio, 1);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
29662306a36Sopenharmony_ci		ret = regulator_set_load(ctx->supplies[i].consumer,
29762306a36Sopenharmony_ci				regulator_disable_loads[i]);
29862306a36Sopenharmony_ci		if (ret) {
29962306a36Sopenharmony_ci			dev_err(ctx->dev, "regulator_set_load failed %d\n", ret);
30062306a36Sopenharmony_ci			return ret;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
30562306a36Sopenharmony_ci	if (ret) {
30662306a36Sopenharmony_ci		dev_err(ctx->dev, "regulator_bulk_disable failed %d\n", ret);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	return ret;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int truly_nt35597_disable(struct drm_panel *panel)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
31462306a36Sopenharmony_ci	int ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!ctx->enabled)
31762306a36Sopenharmony_ci		return 0;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (ctx->backlight) {
32062306a36Sopenharmony_ci		ret = backlight_disable(ctx->backlight);
32162306a36Sopenharmony_ci		if (ret < 0)
32262306a36Sopenharmony_ci			dev_err(ctx->dev, "backlight disable failed %d\n", ret);
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ctx->enabled = false;
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int truly_nt35597_unprepare(struct drm_panel *panel)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
33262306a36Sopenharmony_ci	int ret = 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (!ctx->prepared)
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ctx->dsi[0]->mode_flags = 0;
33862306a36Sopenharmony_ci	ctx->dsi[1]->mode_flags = 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_OFF);
34162306a36Sopenharmony_ci	if (ret < 0) {
34262306a36Sopenharmony_ci		dev_err(ctx->dev, "set_display_off cmd failed ret = %d\n", ret);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* 120ms delay required here as per DCS spec */
34662306a36Sopenharmony_ci	msleep(120);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = truly_dcs_write(panel, MIPI_DCS_ENTER_SLEEP_MODE);
34962306a36Sopenharmony_ci	if (ret < 0) {
35062306a36Sopenharmony_ci		dev_err(ctx->dev, "enter_sleep cmd failed ret = %d\n", ret);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = truly_nt35597_power_off(ctx);
35462306a36Sopenharmony_ci	if (ret < 0)
35562306a36Sopenharmony_ci		dev_err(ctx->dev, "power_off failed ret = %d\n", ret);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ctx->prepared = false;
35862306a36Sopenharmony_ci	return ret;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int truly_nt35597_prepare(struct drm_panel *panel)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
36462306a36Sopenharmony_ci	int ret;
36562306a36Sopenharmony_ci	int i;
36662306a36Sopenharmony_ci	const struct cmd_set *panel_on_cmds;
36762306a36Sopenharmony_ci	const struct nt35597_config *config;
36862306a36Sopenharmony_ci	u32 num_cmds;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (ctx->prepared)
37162306a36Sopenharmony_ci		return 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	ret = truly_35597_power_on(ctx);
37462306a36Sopenharmony_ci	if (ret < 0)
37562306a36Sopenharmony_ci		return ret;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
37862306a36Sopenharmony_ci	ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	config = ctx->config;
38162306a36Sopenharmony_ci	panel_on_cmds = config->panel_on_cmds;
38262306a36Sopenharmony_ci	num_cmds = config->num_on_cmds;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	for (i = 0; i < num_cmds; i++) {
38562306a36Sopenharmony_ci		ret = truly_dcs_write_buf(panel,
38662306a36Sopenharmony_ci				panel_on_cmds[i].size,
38762306a36Sopenharmony_ci					panel_on_cmds[i].commands);
38862306a36Sopenharmony_ci		if (ret < 0) {
38962306a36Sopenharmony_ci			dev_err(ctx->dev, "cmd set tx failed i = %d ret = %d\n", i, ret);
39062306a36Sopenharmony_ci			goto power_off;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = truly_dcs_write(panel, MIPI_DCS_EXIT_SLEEP_MODE);
39562306a36Sopenharmony_ci	if (ret < 0) {
39662306a36Sopenharmony_ci		dev_err(ctx->dev, "exit_sleep_mode cmd failed ret = %d\n", ret);
39762306a36Sopenharmony_ci		goto power_off;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Per DSI spec wait 120ms after sending exit sleep DCS command */
40162306a36Sopenharmony_ci	msleep(120);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_ON);
40462306a36Sopenharmony_ci	if (ret < 0) {
40562306a36Sopenharmony_ci		dev_err(ctx->dev, "set_display_on cmd failed ret = %d\n", ret);
40662306a36Sopenharmony_ci		goto power_off;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Per DSI spec wait 120ms after sending set_display_on DCS command */
41062306a36Sopenharmony_ci	msleep(120);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ctx->prepared = true;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cipower_off:
41762306a36Sopenharmony_ci	if (truly_nt35597_power_off(ctx))
41862306a36Sopenharmony_ci		dev_err(ctx->dev, "power_off failed\n");
41962306a36Sopenharmony_ci	return ret;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int truly_nt35597_enable(struct drm_panel *panel)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
42562306a36Sopenharmony_ci	int ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (ctx->enabled)
42862306a36Sopenharmony_ci		return 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (ctx->backlight) {
43162306a36Sopenharmony_ci		ret = backlight_enable(ctx->backlight);
43262306a36Sopenharmony_ci		if (ret < 0)
43362306a36Sopenharmony_ci			dev_err(ctx->dev, "backlight enable failed %d\n", ret);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	ctx->enabled = true;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic int truly_nt35597_get_modes(struct drm_panel *panel,
44262306a36Sopenharmony_ci				   struct drm_connector *connector)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct truly_nt35597 *ctx = panel_to_ctx(panel);
44562306a36Sopenharmony_ci	struct drm_display_mode *mode;
44662306a36Sopenharmony_ci	const struct nt35597_config *config;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	config = ctx->config;
44962306a36Sopenharmony_ci	mode = drm_mode_duplicate(connector->dev, config->dm);
45062306a36Sopenharmony_ci	if (!mode) {
45162306a36Sopenharmony_ci		dev_err(ctx->dev, "failed to create a new display mode\n");
45262306a36Sopenharmony_ci		return 0;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	connector->display_info.width_mm = config->width_mm;
45662306a36Sopenharmony_ci	connector->display_info.height_mm = config->height_mm;
45762306a36Sopenharmony_ci	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
45862306a36Sopenharmony_ci	drm_mode_probed_add(connector, mode);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return 1;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic const struct drm_panel_funcs truly_nt35597_drm_funcs = {
46462306a36Sopenharmony_ci	.disable = truly_nt35597_disable,
46562306a36Sopenharmony_ci	.unprepare = truly_nt35597_unprepare,
46662306a36Sopenharmony_ci	.prepare = truly_nt35597_prepare,
46762306a36Sopenharmony_ci	.enable = truly_nt35597_enable,
46862306a36Sopenharmony_ci	.get_modes = truly_nt35597_get_modes,
46962306a36Sopenharmony_ci};
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic int truly_nt35597_panel_add(struct truly_nt35597 *ctx)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct device *dev = ctx->dev;
47462306a36Sopenharmony_ci	int ret, i;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
47762306a36Sopenharmony_ci		ctx->supplies[i].supply = regulator_names[i];
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
48062306a36Sopenharmony_ci				      ctx->supplies);
48162306a36Sopenharmony_ci	if (ret < 0)
48262306a36Sopenharmony_ci		return ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
48562306a36Sopenharmony_ci	if (IS_ERR(ctx->reset_gpio)) {
48662306a36Sopenharmony_ci		dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio));
48762306a36Sopenharmony_ci		return PTR_ERR(ctx->reset_gpio);
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	ctx->mode_gpio = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);
49162306a36Sopenharmony_ci	if (IS_ERR(ctx->mode_gpio)) {
49262306a36Sopenharmony_ci		dev_err(dev, "cannot get mode gpio %ld\n", PTR_ERR(ctx->mode_gpio));
49362306a36Sopenharmony_ci		return PTR_ERR(ctx->mode_gpio);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* dual port */
49762306a36Sopenharmony_ci	gpiod_set_value(ctx->mode_gpio, 0);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	drm_panel_init(&ctx->panel, dev, &truly_nt35597_drm_funcs,
50062306a36Sopenharmony_ci		       DRM_MODE_CONNECTOR_DSI);
50162306a36Sopenharmony_ci	drm_panel_add(&ctx->panel);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic const struct drm_display_mode qcom_sdm845_mtp_2k_mode = {
50762306a36Sopenharmony_ci	.name = "1440x2560",
50862306a36Sopenharmony_ci	.clock = 268316,
50962306a36Sopenharmony_ci	.hdisplay = 1440,
51062306a36Sopenharmony_ci	.hsync_start = 1440 + 200,
51162306a36Sopenharmony_ci	.hsync_end = 1440 + 200 + 32,
51262306a36Sopenharmony_ci	.htotal = 1440 + 200 + 32 + 64,
51362306a36Sopenharmony_ci	.vdisplay = 2560,
51462306a36Sopenharmony_ci	.vsync_start = 2560 + 8,
51562306a36Sopenharmony_ci	.vsync_end = 2560 + 8 + 1,
51662306a36Sopenharmony_ci	.vtotal = 2560 + 8 + 1 + 7,
51762306a36Sopenharmony_ci	.flags = 0,
51862306a36Sopenharmony_ci};
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic const struct nt35597_config nt35597_dir = {
52162306a36Sopenharmony_ci	.width_mm = 74,
52262306a36Sopenharmony_ci	.height_mm = 131,
52362306a36Sopenharmony_ci	.panel_name = "qcom_sdm845_mtp_2k_panel",
52462306a36Sopenharmony_ci	.dm = &qcom_sdm845_mtp_2k_mode,
52562306a36Sopenharmony_ci	.panel_on_cmds = qcom_2k_panel_magic_cmds,
52662306a36Sopenharmony_ci	.num_on_cmds = ARRAY_SIZE(qcom_2k_panel_magic_cmds),
52762306a36Sopenharmony_ci};
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int truly_nt35597_probe(struct mipi_dsi_device *dsi)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct device *dev = &dsi->dev;
53262306a36Sopenharmony_ci	struct truly_nt35597 *ctx;
53362306a36Sopenharmony_ci	struct mipi_dsi_device *dsi1_device;
53462306a36Sopenharmony_ci	struct device_node *dsi1;
53562306a36Sopenharmony_ci	struct mipi_dsi_host *dsi1_host;
53662306a36Sopenharmony_ci	struct mipi_dsi_device *dsi_dev;
53762306a36Sopenharmony_ci	int ret = 0;
53862306a36Sopenharmony_ci	int i;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	const struct mipi_dsi_device_info info = {
54162306a36Sopenharmony_ci		.type = "trulynt35597",
54262306a36Sopenharmony_ci		.channel = 0,
54362306a36Sopenharmony_ci		.node = NULL,
54462306a36Sopenharmony_ci	};
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (!ctx)
54962306a36Sopenharmony_ci		return -ENOMEM;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/*
55262306a36Sopenharmony_ci	 * This device represents itself as one with two input ports which are
55362306a36Sopenharmony_ci	 * fed by the output ports of the two DSI controllers . The DSI0 is
55462306a36Sopenharmony_ci	 * the master controller and has most of the panel related info in its
55562306a36Sopenharmony_ci	 * child node.
55662306a36Sopenharmony_ci	 */
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ctx->config = of_device_get_match_data(dev);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!ctx->config) {
56162306a36Sopenharmony_ci		dev_err(dev, "missing device configuration\n");
56262306a36Sopenharmony_ci		return -ENODEV;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
56662306a36Sopenharmony_ci	if (!dsi1) {
56762306a36Sopenharmony_ci		dev_err(dev, "failed to get remote node for dsi1_device\n");
56862306a36Sopenharmony_ci		return -ENODEV;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
57262306a36Sopenharmony_ci	of_node_put(dsi1);
57362306a36Sopenharmony_ci	if (!dsi1_host) {
57462306a36Sopenharmony_ci		dev_err(dev, "failed to find dsi host\n");
57562306a36Sopenharmony_ci		return -EPROBE_DEFER;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* register the second DSI device */
57962306a36Sopenharmony_ci	dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info);
58062306a36Sopenharmony_ci	if (IS_ERR(dsi1_device)) {
58162306a36Sopenharmony_ci		dev_err(dev, "failed to create dsi device\n");
58262306a36Sopenharmony_ci		return PTR_ERR(dsi1_device);
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	mipi_dsi_set_drvdata(dsi, ctx);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ctx->dev = dev;
58862306a36Sopenharmony_ci	ctx->dsi[0] = dsi;
58962306a36Sopenharmony_ci	ctx->dsi[1] = dsi1_device;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	ret = truly_nt35597_panel_add(ctx);
59262306a36Sopenharmony_ci	if (ret) {
59362306a36Sopenharmony_ci		dev_err(dev, "failed to add panel\n");
59462306a36Sopenharmony_ci		goto err_panel_add;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
59862306a36Sopenharmony_ci		dsi_dev = ctx->dsi[i];
59962306a36Sopenharmony_ci		dsi_dev->lanes = 4;
60062306a36Sopenharmony_ci		dsi_dev->format = MIPI_DSI_FMT_RGB888;
60162306a36Sopenharmony_ci		dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
60262306a36Sopenharmony_ci			MIPI_DSI_CLOCK_NON_CONTINUOUS;
60362306a36Sopenharmony_ci		ret = mipi_dsi_attach(dsi_dev);
60462306a36Sopenharmony_ci		if (ret < 0) {
60562306a36Sopenharmony_ci			dev_err(dev, "dsi attach failed i = %d\n", i);
60662306a36Sopenharmony_ci			goto err_dsi_attach;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cierr_dsi_attach:
61362306a36Sopenharmony_ci	drm_panel_remove(&ctx->panel);
61462306a36Sopenharmony_cierr_panel_add:
61562306a36Sopenharmony_ci	mipi_dsi_device_unregister(dsi1_device);
61662306a36Sopenharmony_ci	return ret;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic void truly_nt35597_remove(struct mipi_dsi_device *dsi)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (ctx->dsi[0])
62462306a36Sopenharmony_ci		mipi_dsi_detach(ctx->dsi[0]);
62562306a36Sopenharmony_ci	if (ctx->dsi[1]) {
62662306a36Sopenharmony_ci		mipi_dsi_detach(ctx->dsi[1]);
62762306a36Sopenharmony_ci		mipi_dsi_device_unregister(ctx->dsi[1]);
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	drm_panel_remove(&ctx->panel);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic const struct of_device_id truly_nt35597_of_match[] = {
63462306a36Sopenharmony_ci	{
63562306a36Sopenharmony_ci		.compatible = "truly,nt35597-2K-display",
63662306a36Sopenharmony_ci		.data = &nt35597_dir,
63762306a36Sopenharmony_ci	},
63862306a36Sopenharmony_ci	{ }
63962306a36Sopenharmony_ci};
64062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, truly_nt35597_of_match);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic struct mipi_dsi_driver truly_nt35597_driver = {
64362306a36Sopenharmony_ci	.driver = {
64462306a36Sopenharmony_ci		.name = "panel-truly-nt35597",
64562306a36Sopenharmony_ci		.of_match_table = truly_nt35597_of_match,
64662306a36Sopenharmony_ci	},
64762306a36Sopenharmony_ci	.probe = truly_nt35597_probe,
64862306a36Sopenharmony_ci	.remove = truly_nt35597_remove,
64962306a36Sopenharmony_ci};
65062306a36Sopenharmony_cimodule_mipi_dsi_driver(truly_nt35597_driver);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ciMODULE_DESCRIPTION("Truly NT35597 DSI Panel Driver");
65362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
654