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