162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Novatek NT35510 panel driver 462306a36Sopenharmony_ci * Copyright (C) 2020 Linus Walleij <linus.walleij@linaro.org> 562306a36Sopenharmony_ci * Based on code by Robert Teather (C) 2012 Samsung 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This display driver (and I refer to the physical component NT35510, 862306a36Sopenharmony_ci * not this Linux kernel software driver) can handle: 962306a36Sopenharmony_ci * 480x864, 480x854, 480x800, 480x720 and 480x640 pixel displays. 1062306a36Sopenharmony_ci * It has 480x840x24bit SRAM embedded for storing a frame. 1162306a36Sopenharmony_ci * When powered on the display is by default in 480x800 mode. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The actual panels using this component have different names, but 1462306a36Sopenharmony_ci * the code needed to set up and configure the panel will be similar, 1562306a36Sopenharmony_ci * so they should all use the NT35510 driver with appropriate configuration 1662306a36Sopenharmony_ci * per-panel, e.g. for physical size. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * This driver is for the DSI interface to panels using the NT35510. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * The NT35510 can also use an RGB (DPI) interface combined with an 2162306a36Sopenharmony_ci * I2C or SPI interface for setting up the NT35510. If this is needed 2262306a36Sopenharmony_ci * this panel driver should be refactored to also support that use 2362306a36Sopenharmony_ci * case. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci#include <linux/backlight.h> 2662306a36Sopenharmony_ci#include <linux/bitops.h> 2762306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/regmap.h> 3162306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <video/mipi_display.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 3662306a36Sopenharmony_ci#include <drm/drm_modes.h> 3762306a36Sopenharmony_ci#include <drm/drm_panel.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MCS_CMD_MAUCCTR 0xF0 /* Manufacturer command enable */ 4062306a36Sopenharmony_ci#define MCS_CMD_READ_ID1 0xDA 4162306a36Sopenharmony_ci#define MCS_CMD_READ_ID2 0xDB 4262306a36Sopenharmony_ci#define MCS_CMD_READ_ID3 0xDC 4362306a36Sopenharmony_ci#define MCS_CMD_MTP_READ_SETTING 0xF8 /* Uncertain about name */ 4462306a36Sopenharmony_ci#define MCS_CMD_MTP_READ_PARAM 0xFF /* Uncertain about name */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * These manufacturer commands are available after we enable manufacturer 4862306a36Sopenharmony_ci * command set (MCS) for page 0. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define NT35510_P0_DOPCTR 0xB1 5162306a36Sopenharmony_ci#define NT35510_P0_SDHDTCTR 0xB6 5262306a36Sopenharmony_ci#define NT35510_P0_GSEQCTR 0xB7 5362306a36Sopenharmony_ci#define NT35510_P0_SDEQCTR 0xB8 5462306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR 0xBA 5562306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR1 0xBD 5662306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR2 0xBE 5762306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR3 0xBF 5862306a36Sopenharmony_ci#define NT35510_P0_DPMCTR12 0xCC 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define NT35510_P0_DOPCTR_LEN 2 6162306a36Sopenharmony_ci#define NT35510_P0_GSEQCTR_LEN 2 6262306a36Sopenharmony_ci#define NT35510_P0_SDEQCTR_LEN 4 6362306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_LEN 1 6462306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR1_LEN 5 6562306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR2_LEN 5 6662306a36Sopenharmony_ci#define NT35510_P0_DPFRCTR3_LEN 5 6762306a36Sopenharmony_ci#define NT35510_P0_DPMCTR12_LEN 3 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define NT35510_DOPCTR_0_RAMKP BIT(7) /* Contents kept in sleep */ 7062306a36Sopenharmony_ci#define NT35510_DOPCTR_0_DSITE BIT(6) /* Enable TE signal */ 7162306a36Sopenharmony_ci#define NT35510_DOPCTR_0_DSIG BIT(5) /* Enable generic read/write */ 7262306a36Sopenharmony_ci#define NT35510_DOPCTR_0_DSIM BIT(4) /* Enable video mode on DSI */ 7362306a36Sopenharmony_ci#define NT35510_DOPCTR_0_EOTP BIT(3) /* Support EoTP */ 7462306a36Sopenharmony_ci#define NT35510_DOPCTR_0_N565 BIT(2) /* RGB or BGR pixel format */ 7562306a36Sopenharmony_ci#define NT35510_DOPCTR_1_TW_PWR_SEL BIT(4) /* TE power selector */ 7662306a36Sopenharmony_ci#define NT35510_DOPCTR_1_CRGB BIT(3) /* RGB or BGR byte order */ 7762306a36Sopenharmony_ci#define NT35510_DOPCTR_1_CTB BIT(2) /* Vertical scanning direction */ 7862306a36Sopenharmony_ci#define NT35510_DOPCTR_1_CRL BIT(1) /* Source driver data shift */ 7962306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_PRG BIT(2) /* 0 = normal operation, 1 = VGLO */ 8062306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_AVDD 0 /* source driver output = AVDD */ 8162306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_OFFCOL 1 /* source driver output = off color */ 8262306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_AVSS 2 /* source driver output = AVSS */ 8362306a36Sopenharmony_ci#define NT35510_P0_SDVPCTR_HI_Z 3 /* source driver output = High impedance */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * These manufacturer commands are available after we enable manufacturer 8762306a36Sopenharmony_ci * command set (MCS) for page 1. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci#define NT35510_P1_SETAVDD 0xB0 9062306a36Sopenharmony_ci#define NT35510_P1_SETAVEE 0xB1 9162306a36Sopenharmony_ci#define NT35510_P1_SETVCL 0xB2 9262306a36Sopenharmony_ci#define NT35510_P1_SETVGH 0xB3 9362306a36Sopenharmony_ci#define NT35510_P1_SETVRGH 0xB4 9462306a36Sopenharmony_ci#define NT35510_P1_SETVGL 0xB5 9562306a36Sopenharmony_ci#define NT35510_P1_BT1CTR 0xB6 9662306a36Sopenharmony_ci#define NT35510_P1_BT2CTR 0xB7 9762306a36Sopenharmony_ci#define NT35510_P1_BT3CTR 0xB8 9862306a36Sopenharmony_ci#define NT35510_P1_BT4CTR 0xB9 /* VGH boosting times/freq */ 9962306a36Sopenharmony_ci#define NT35510_P1_BT5CTR 0xBA 10062306a36Sopenharmony_ci#define NT35510_P1_PFMCTR 0xBB 10162306a36Sopenharmony_ci#define NT35510_P1_SETVGP 0xBC 10262306a36Sopenharmony_ci#define NT35510_P1_SETVGN 0xBD 10362306a36Sopenharmony_ci#define NT35510_P1_SETVCMOFF 0xBE 10462306a36Sopenharmony_ci#define NT35510_P1_VGHCTR 0xBF /* VGH output ctrl */ 10562306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_RED_POS 0xD1 10662306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_GREEN_POS 0xD2 10762306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_BLUE_POS 0xD3 10862306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_RED_NEG 0xD4 10962306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_GREEN_NEG 0xD5 11062306a36Sopenharmony_ci#define NT35510_P1_SET_GAMMA_BLUE_NEG 0xD6 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* AVDD and AVEE setting 3 bytes */ 11362306a36Sopenharmony_ci#define NT35510_P1_AVDD_LEN 3 11462306a36Sopenharmony_ci#define NT35510_P1_AVEE_LEN 3 11562306a36Sopenharmony_ci#define NT35510_P1_VGH_LEN 3 11662306a36Sopenharmony_ci#define NT35510_P1_VGL_LEN 3 11762306a36Sopenharmony_ci#define NT35510_P1_VGP_LEN 3 11862306a36Sopenharmony_ci#define NT35510_P1_VGN_LEN 3 11962306a36Sopenharmony_ci/* BT1CTR thru BT5CTR setting 3 bytes */ 12062306a36Sopenharmony_ci#define NT35510_P1_BT1CTR_LEN 3 12162306a36Sopenharmony_ci#define NT35510_P1_BT2CTR_LEN 3 12262306a36Sopenharmony_ci#define NT35510_P1_BT4CTR_LEN 3 12362306a36Sopenharmony_ci#define NT35510_P1_BT5CTR_LEN 3 12462306a36Sopenharmony_ci/* 52 gamma parameters times two per color: positive and negative */ 12562306a36Sopenharmony_ci#define NT35510_P1_GAMMA_LEN 52 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * struct nt35510_config - the display-specific NT35510 configuration 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Some of the settings provide an array of bytes, A, B C which mean: 13162306a36Sopenharmony_ci * A = normal / idle off mode 13262306a36Sopenharmony_ci * B = idle on mode 13362306a36Sopenharmony_ci * C = partial / idle off mode 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * Gamma correction arrays are 10bit numbers, two consecutive bytes 13662306a36Sopenharmony_ci * makes out one point on the gamma correction curve. The points are 13762306a36Sopenharmony_ci * not linearly placed along the X axis, we get points 0, 1, 3, 5 13862306a36Sopenharmony_ci * 7, 11, 15, 23, 31, 47, 63, 95, 127, 128, 160, 192, 208, 224, 232, 13962306a36Sopenharmony_ci * 240, 244, 248, 250, 252, 254, 255. The voltages tuples form 14062306a36Sopenharmony_ci * V0, V1, V3 ... V255, with 0x0000 being the lowest voltage and 14162306a36Sopenharmony_ci * 0x03FF being the highest voltage. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Each value must be strictly higher than the previous value forming 14462306a36Sopenharmony_ci * a rising curve like this: 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * ^ 14762306a36Sopenharmony_ci * | V255 14862306a36Sopenharmony_ci * | V254 14962306a36Sopenharmony_ci * | .... 15062306a36Sopenharmony_ci * | V5 15162306a36Sopenharmony_ci * | V3 15262306a36Sopenharmony_ci * | V1 15362306a36Sopenharmony_ci * | V0 15462306a36Sopenharmony_ci * +-------------------------------------------> 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * The details about all settings can be found in the NT35510 Application 15762306a36Sopenharmony_ci * Note. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistruct nt35510_config { 16062306a36Sopenharmony_ci /** 16162306a36Sopenharmony_ci * @width_mm: physical panel width [mm] 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci u32 width_mm; 16462306a36Sopenharmony_ci /** 16562306a36Sopenharmony_ci * @height_mm: physical panel height [mm] 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci u32 height_mm; 16862306a36Sopenharmony_ci /** 16962306a36Sopenharmony_ci * @mode: the display mode. This is only relevant outside the panel 17062306a36Sopenharmony_ci * in video mode: in command mode this is configuring the internal 17162306a36Sopenharmony_ci * timing in the display controller. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci const struct drm_display_mode mode; 17462306a36Sopenharmony_ci /** 17562306a36Sopenharmony_ci * @avdd: setting for AVDD ranging from 0x00 = 6.5V to 0x14 = 4.5V 17662306a36Sopenharmony_ci * in 0.1V steps the default is 0x05 which means 6.0V 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci u8 avdd[NT35510_P1_AVDD_LEN]; 17962306a36Sopenharmony_ci /** 18062306a36Sopenharmony_ci * @bt1ctr: setting for boost power control for the AVDD step-up 18162306a36Sopenharmony_ci * circuit (1) 18262306a36Sopenharmony_ci * bits 0..2 in the lower nibble controls PCK, the booster clock 18362306a36Sopenharmony_ci * frequency for the step-up circuit: 18462306a36Sopenharmony_ci * 0 = Hsync/32 18562306a36Sopenharmony_ci * 1 = Hsync/16 18662306a36Sopenharmony_ci * 2 = Hsync/8 18762306a36Sopenharmony_ci * 3 = Hsync/4 18862306a36Sopenharmony_ci * 4 = Hsync/2 18962306a36Sopenharmony_ci * 5 = Hsync 19062306a36Sopenharmony_ci * 6 = Hsync x 2 19162306a36Sopenharmony_ci * 7 = Hsync x 4 19262306a36Sopenharmony_ci * bits 4..6 in the upper nibble controls BTP, the boosting 19362306a36Sopenharmony_ci * amplification for the step-up circuit: 19462306a36Sopenharmony_ci * 0 = Disable 19562306a36Sopenharmony_ci * 1 = 1.5 x VDDB 19662306a36Sopenharmony_ci * 2 = 1.66 x VDDB 19762306a36Sopenharmony_ci * 3 = 2 x VDDB 19862306a36Sopenharmony_ci * 4 = 2.5 x VDDB 19962306a36Sopenharmony_ci * 5 = 3 x VDDB 20062306a36Sopenharmony_ci * The defaults are 4 and 4 yielding 0x44 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci u8 bt1ctr[NT35510_P1_BT1CTR_LEN]; 20362306a36Sopenharmony_ci /** 20462306a36Sopenharmony_ci * @avee: setting for AVEE ranging from 0x00 = -6.5V to 0x14 = -4.5V 20562306a36Sopenharmony_ci * in 0.1V steps the default is 0x05 which means -6.0V 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci u8 avee[NT35510_P1_AVEE_LEN]; 20862306a36Sopenharmony_ci /** 20962306a36Sopenharmony_ci * @bt2ctr: setting for boost power control for the AVEE step-up 21062306a36Sopenharmony_ci * circuit (2) 21162306a36Sopenharmony_ci * bits 0..2 in the lower nibble controls NCK, the booster clock 21262306a36Sopenharmony_ci * frequency, the values are the same as for PCK in @bt1ctr. 21362306a36Sopenharmony_ci * bits 4..5 in the upper nibble controls BTN, the boosting 21462306a36Sopenharmony_ci * amplification for the step-up circuit. 21562306a36Sopenharmony_ci * 0 = Disable 21662306a36Sopenharmony_ci * 1 = -1.5 x VDDB 21762306a36Sopenharmony_ci * 2 = -2 x VDDB 21862306a36Sopenharmony_ci * 3 = -2.5 x VDDB 21962306a36Sopenharmony_ci * 4 = -3 x VDDB 22062306a36Sopenharmony_ci * The defaults are 4 and 3 yielding 0x34 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci u8 bt2ctr[NT35510_P1_BT2CTR_LEN]; 22362306a36Sopenharmony_ci /** 22462306a36Sopenharmony_ci * @vgh: setting for VGH ranging from 0x00 = 7.0V to 0x0B = 18.0V 22562306a36Sopenharmony_ci * in 1V steps, the default is 0x08 which means 15V 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci u8 vgh[NT35510_P1_VGH_LEN]; 22862306a36Sopenharmony_ci /** 22962306a36Sopenharmony_ci * @bt4ctr: setting for boost power control for the VGH step-up 23062306a36Sopenharmony_ci * circuit (4) 23162306a36Sopenharmony_ci * bits 0..2 in the lower nibble controls HCK, the booster clock 23262306a36Sopenharmony_ci * frequency, the values are the same as for PCK in @bt1ctr. 23362306a36Sopenharmony_ci * bits 4..5 in the upper nibble controls BTH, the boosting 23462306a36Sopenharmony_ci * amplification for the step-up circuit. 23562306a36Sopenharmony_ci * 0 = AVDD + VDDB 23662306a36Sopenharmony_ci * 1 = AVDD - AVEE 23762306a36Sopenharmony_ci * 2 = AVDD - AVEE + VDDB 23862306a36Sopenharmony_ci * 3 = AVDD x 2 - AVEE 23962306a36Sopenharmony_ci * The defaults are 4 and 3 yielding 0x34 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci u8 bt4ctr[NT35510_P1_BT4CTR_LEN]; 24262306a36Sopenharmony_ci /** 24362306a36Sopenharmony_ci * @vgl: setting for VGL ranging from 0x00 = -2V to 0x0f = -15V in 24462306a36Sopenharmony_ci * 1V steps, the default is 0x08 which means -10V 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci u8 vgl[NT35510_P1_VGL_LEN]; 24762306a36Sopenharmony_ci /** 24862306a36Sopenharmony_ci * @bt5ctr: setting for boost power control for the VGL step-up 24962306a36Sopenharmony_ci * circuit (5) 25062306a36Sopenharmony_ci * bits 0..2 in the lower nibble controls LCK, the booster clock 25162306a36Sopenharmony_ci * frequency, the values are the same as for PCK in @bt1ctr. 25262306a36Sopenharmony_ci * bits 4..5 in the upper nibble controls BTL, the boosting 25362306a36Sopenharmony_ci * amplification for the step-up circuit. 25462306a36Sopenharmony_ci * 0 = AVEE + VCL 25562306a36Sopenharmony_ci * 1 = AVEE - AVDD 25662306a36Sopenharmony_ci * 2 = AVEE + VCL - AVDD 25762306a36Sopenharmony_ci * 3 = AVEE x 2 - AVDD 25862306a36Sopenharmony_ci * The defaults are 3 and 2 yielding 0x32 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci u8 bt5ctr[NT35510_P1_BT5CTR_LEN]; 26162306a36Sopenharmony_ci /** 26262306a36Sopenharmony_ci * @vgp: setting for VGP, the positive gamma divider voltages 26362306a36Sopenharmony_ci * VGMP the high voltage and VGSP the low voltage. 26462306a36Sopenharmony_ci * The first byte contains bit 8 of VGMP and VGSP in bits 4 and 0 26562306a36Sopenharmony_ci * The second byte contains bit 0..7 of VGMP 26662306a36Sopenharmony_ci * The third byte contains bit 0..7 of VGSP 26762306a36Sopenharmony_ci * VGMP 0x00 = 3.0V .. 0x108 = 6.3V in steps of 12.5mV 26862306a36Sopenharmony_ci * VGSP 0x00 = 0V .. 0x111 = 3.7V in steps of 12.5mV 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci u8 vgp[NT35510_P1_VGP_LEN]; 27162306a36Sopenharmony_ci /** 27262306a36Sopenharmony_ci * @vgn: setting for VGN, the negative gamma divider voltages, 27362306a36Sopenharmony_ci * same layout of bytes as @vgp. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci u8 vgn[NT35510_P1_VGN_LEN]; 27662306a36Sopenharmony_ci /** 27762306a36Sopenharmony_ci * @sdeqctr: Source driver control settings, first byte is 27862306a36Sopenharmony_ci * 0 for mode 1 and 1 for mode 2. Mode 1 uses two steps and 27962306a36Sopenharmony_ci * mode 2 uses three steps meaning EQS3 is not used in mode 28062306a36Sopenharmony_ci * 1. Mode 2 is default. The last three parameters are EQS1, EQS2 28162306a36Sopenharmony_ci * and EQS3, setting the rise time for each equalizer step: 28262306a36Sopenharmony_ci * 0x00 = 0.0 us to 0x0f = 7.5 us in steps of 0.5us. The default 28362306a36Sopenharmony_ci * is 0x07 = 3.5 us. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci u8 sdeqctr[NT35510_P0_SDEQCTR_LEN]; 28662306a36Sopenharmony_ci /** 28762306a36Sopenharmony_ci * @sdvpctr: power/voltage behaviour during vertical porch time 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci u8 sdvpctr; 29062306a36Sopenharmony_ci /** 29162306a36Sopenharmony_ci * @t1: the number of pixel clocks on one scanline, range 29262306a36Sopenharmony_ci * 0x100 (258 ticks) .. 0x3FF (1024 ticks) so the value + 1 29362306a36Sopenharmony_ci * clock ticks. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci u16 t1; 29662306a36Sopenharmony_ci /** 29762306a36Sopenharmony_ci * @vbp: vertical back porch toward the PANEL note: not toward 29862306a36Sopenharmony_ci * the DSI host; these are separate interfaces, in from DSI host 29962306a36Sopenharmony_ci * and out to the panel. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci u8 vbp; 30262306a36Sopenharmony_ci /** 30362306a36Sopenharmony_ci * @vfp: vertical front porch toward the PANEL. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci u8 vfp; 30662306a36Sopenharmony_ci /** 30762306a36Sopenharmony_ci * @psel: pixel clock divisor: 0 = 1, 1 = 2, 2 = 4, 3 = 8. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci u8 psel; 31062306a36Sopenharmony_ci /** 31162306a36Sopenharmony_ci * @dpmctr12: Display timing control 12 31262306a36Sopenharmony_ci * Byte 1 bit 4 selects LVGL voltage level: 0 = VGLX, 1 = VGL_REG 31362306a36Sopenharmony_ci * Byte 1 bit 1 selects gate signal mode: 0 = non-overlap, 1 = overlap 31462306a36Sopenharmony_ci * Byte 1 bit 0 selects output signal control R/L swap, 0 = normal 31562306a36Sopenharmony_ci * 1 = swap all O->E, L->R 31662306a36Sopenharmony_ci * Byte 2 is CLW delay clock for CK O/E and CKB O/E signals: 31762306a36Sopenharmony_ci * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps 31862306a36Sopenharmony_ci * Byte 3 is FTI_H0 delay time for STP O/E signals: 31962306a36Sopenharmony_ci * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci u8 dpmctr12[NT35510_P0_DPMCTR12_LEN]; 32262306a36Sopenharmony_ci /** 32362306a36Sopenharmony_ci * @gamma_corr_pos_r: Red gamma correction parameters, positive 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci u8 gamma_corr_pos_r[NT35510_P1_GAMMA_LEN]; 32662306a36Sopenharmony_ci /** 32762306a36Sopenharmony_ci * @gamma_corr_pos_g: Green gamma correction parameters, positive 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci u8 gamma_corr_pos_g[NT35510_P1_GAMMA_LEN]; 33062306a36Sopenharmony_ci /** 33162306a36Sopenharmony_ci * @gamma_corr_pos_b: Blue gamma correction parameters, positive 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci u8 gamma_corr_pos_b[NT35510_P1_GAMMA_LEN]; 33462306a36Sopenharmony_ci /** 33562306a36Sopenharmony_ci * @gamma_corr_neg_r: Red gamma correction parameters, negative 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci u8 gamma_corr_neg_r[NT35510_P1_GAMMA_LEN]; 33862306a36Sopenharmony_ci /** 33962306a36Sopenharmony_ci * @gamma_corr_neg_g: Green gamma correction parameters, negative 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci u8 gamma_corr_neg_g[NT35510_P1_GAMMA_LEN]; 34262306a36Sopenharmony_ci /** 34362306a36Sopenharmony_ci * @gamma_corr_neg_b: Blue gamma correction parameters, negative 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci u8 gamma_corr_neg_b[NT35510_P1_GAMMA_LEN]; 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/** 34962306a36Sopenharmony_ci * struct nt35510 - state container for the NT35510 panel 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistruct nt35510 { 35262306a36Sopenharmony_ci /** 35362306a36Sopenharmony_ci * @dev: the container device 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci struct device *dev; 35662306a36Sopenharmony_ci /** 35762306a36Sopenharmony_ci * @conf: the specific panel configuration, as the NT35510 35862306a36Sopenharmony_ci * can be combined with many physical panels, they can have 35962306a36Sopenharmony_ci * different physical dimensions and gamma correction etc, 36062306a36Sopenharmony_ci * so this is stored in the config. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci const struct nt35510_config *conf; 36362306a36Sopenharmony_ci /** 36462306a36Sopenharmony_ci * @panel: the DRM panel object for the instance 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci struct drm_panel panel; 36762306a36Sopenharmony_ci /** 36862306a36Sopenharmony_ci * @supplies: regulators supplying the panel 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci struct regulator_bulk_data supplies[2]; 37162306a36Sopenharmony_ci /** 37262306a36Sopenharmony_ci * @reset_gpio: the reset line 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* Manufacturer command has strictly this byte sequence */ 37862306a36Sopenharmony_cistatic const u8 nt35510_mauc_mtp_read_param[] = { 0xAA, 0x55, 0x25, 0x01 }; 37962306a36Sopenharmony_cistatic const u8 nt35510_mauc_mtp_read_setting[] = { 0x01, 0x02, 0x00, 0x20, 38062306a36Sopenharmony_ci 0x33, 0x13, 0x00, 0x40, 38162306a36Sopenharmony_ci 0x00, 0x00, 0x23, 0x02 }; 38262306a36Sopenharmony_cistatic const u8 nt35510_mauc_select_page_0[] = { 0x55, 0xAA, 0x52, 0x08, 0x00 }; 38362306a36Sopenharmony_cistatic const u8 nt35510_mauc_select_page_1[] = { 0x55, 0xAA, 0x52, 0x08, 0x01 }; 38462306a36Sopenharmony_cistatic const u8 nt35510_vgh_on[] = { 0x01 }; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic inline struct nt35510 *panel_to_nt35510(struct drm_panel *panel) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci return container_of(panel, struct nt35510, panel); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci#define NT35510_ROTATE_0_SETTING 0x02 39262306a36Sopenharmony_ci#define NT35510_ROTATE_180_SETTING 0x00 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int nt35510_send_long(struct nt35510 *nt, struct mipi_dsi_device *dsi, 39562306a36Sopenharmony_ci u8 cmd, u8 cmdlen, const u8 *seq) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci const u8 *seqp = seq; 39862306a36Sopenharmony_ci int cmdwritten = 0; 39962306a36Sopenharmony_ci int chunk = cmdlen; 40062306a36Sopenharmony_ci int ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (chunk > 15) 40362306a36Sopenharmony_ci chunk = 15; 40462306a36Sopenharmony_ci ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk); 40562306a36Sopenharmony_ci if (ret < 0) { 40662306a36Sopenharmony_ci dev_err(nt->dev, "error sending DCS command seq cmd %02x\n", cmd); 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci cmdwritten += chunk; 41062306a36Sopenharmony_ci seqp += chunk; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci while (cmdwritten < cmdlen) { 41362306a36Sopenharmony_ci chunk = cmdlen - cmdwritten; 41462306a36Sopenharmony_ci if (chunk > 15) 41562306a36Sopenharmony_ci chunk = 15; 41662306a36Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, seqp, chunk); 41762306a36Sopenharmony_ci if (ret < 0) { 41862306a36Sopenharmony_ci dev_err(nt->dev, "error sending generic write seq %02x\n", cmd); 41962306a36Sopenharmony_ci return ret; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci cmdwritten += chunk; 42262306a36Sopenharmony_ci seqp += chunk; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci dev_dbg(nt->dev, "sent command %02x %02x bytes\n", cmd, cmdlen); 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int nt35510_read_id(struct nt35510 *nt) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 43162306a36Sopenharmony_ci u8 id1, id2, id3; 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID1, &id1, 1); 43562306a36Sopenharmony_ci if (ret < 0) { 43662306a36Sopenharmony_ci dev_err(nt->dev, "could not read MTP ID1\n"); 43762306a36Sopenharmony_ci return ret; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID2, &id2, 1); 44062306a36Sopenharmony_ci if (ret < 0) { 44162306a36Sopenharmony_ci dev_err(nt->dev, "could not read MTP ID2\n"); 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID3, &id3, 1); 44562306a36Sopenharmony_ci if (ret < 0) { 44662306a36Sopenharmony_ci dev_err(nt->dev, "could not read MTP ID3\n"); 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Multi-Time Programmable (?) memory contains manufacturer 45262306a36Sopenharmony_ci * ID (e.g. Hydis 0x55), driver ID (e.g. NT35510 0xc0) and 45362306a36Sopenharmony_ci * version. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci dev_info(nt->dev, "MTP ID manufacturer: %02x version: %02x driver: %02x\n", id1, id2, id3); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/** 46162306a36Sopenharmony_ci * nt35510_setup_power() - set up power config in page 1 46262306a36Sopenharmony_ci * @nt: the display instance to set up 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_cistatic int nt35510_setup_power(struct nt35510 *nt) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 46762306a36Sopenharmony_ci int ret; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETAVDD, 47062306a36Sopenharmony_ci NT35510_P1_AVDD_LEN, 47162306a36Sopenharmony_ci nt->conf->avdd); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_BT1CTR, 47562306a36Sopenharmony_ci NT35510_P1_BT1CTR_LEN, 47662306a36Sopenharmony_ci nt->conf->bt1ctr); 47762306a36Sopenharmony_ci if (ret) 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETAVEE, 48062306a36Sopenharmony_ci NT35510_P1_AVEE_LEN, 48162306a36Sopenharmony_ci nt->conf->avee); 48262306a36Sopenharmony_ci if (ret) 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_BT2CTR, 48562306a36Sopenharmony_ci NT35510_P1_BT2CTR_LEN, 48662306a36Sopenharmony_ci nt->conf->bt2ctr); 48762306a36Sopenharmony_ci if (ret) 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGH, 49062306a36Sopenharmony_ci NT35510_P1_VGH_LEN, 49162306a36Sopenharmony_ci nt->conf->vgh); 49262306a36Sopenharmony_ci if (ret) 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_BT4CTR, 49562306a36Sopenharmony_ci NT35510_P1_BT4CTR_LEN, 49662306a36Sopenharmony_ci nt->conf->bt4ctr); 49762306a36Sopenharmony_ci if (ret) 49862306a36Sopenharmony_ci return ret; 49962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_VGHCTR, 50062306a36Sopenharmony_ci ARRAY_SIZE(nt35510_vgh_on), 50162306a36Sopenharmony_ci nt35510_vgh_on); 50262306a36Sopenharmony_ci if (ret) 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGL, 50562306a36Sopenharmony_ci NT35510_P1_VGL_LEN, 50662306a36Sopenharmony_ci nt->conf->vgl); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_BT5CTR, 51062306a36Sopenharmony_ci NT35510_P1_BT5CTR_LEN, 51162306a36Sopenharmony_ci nt->conf->bt5ctr); 51262306a36Sopenharmony_ci if (ret) 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGP, 51562306a36Sopenharmony_ci NT35510_P1_VGP_LEN, 51662306a36Sopenharmony_ci nt->conf->vgp); 51762306a36Sopenharmony_ci if (ret) 51862306a36Sopenharmony_ci return ret; 51962306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGN, 52062306a36Sopenharmony_ci NT35510_P1_VGN_LEN, 52162306a36Sopenharmony_ci nt->conf->vgn); 52262306a36Sopenharmony_ci if (ret) 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Typically 10 ms */ 52662306a36Sopenharmony_ci usleep_range(10000, 20000); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/** 53262306a36Sopenharmony_ci * nt35510_setup_display() - set up display config in page 0 53362306a36Sopenharmony_ci * @nt: the display instance to set up 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_cistatic int nt35510_setup_display(struct nt35510 *nt) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 53862306a36Sopenharmony_ci const struct nt35510_config *conf = nt->conf; 53962306a36Sopenharmony_ci u8 dopctr[NT35510_P0_DOPCTR_LEN]; 54062306a36Sopenharmony_ci u8 gseqctr[NT35510_P0_GSEQCTR_LEN]; 54162306a36Sopenharmony_ci u8 dpfrctr[NT35510_P0_DPFRCTR1_LEN]; 54262306a36Sopenharmony_ci /* FIXME: set up any rotation (assume none for now) */ 54362306a36Sopenharmony_ci u8 addr_mode = NT35510_ROTATE_0_SETTING; 54462306a36Sopenharmony_ci u8 val; 54562306a36Sopenharmony_ci int ret; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Enable TE, EoTP and RGB pixel format */ 54862306a36Sopenharmony_ci dopctr[0] = NT35510_DOPCTR_0_DSITE | NT35510_DOPCTR_0_EOTP | 54962306a36Sopenharmony_ci NT35510_DOPCTR_0_N565; 55062306a36Sopenharmony_ci dopctr[1] = NT35510_DOPCTR_1_CTB; 55162306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_DOPCTR, 55262306a36Sopenharmony_ci NT35510_P0_DOPCTR_LEN, 55362306a36Sopenharmony_ci dopctr); 55462306a36Sopenharmony_ci if (ret) 55562306a36Sopenharmony_ci return ret; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &addr_mode, 55862306a36Sopenharmony_ci sizeof(addr_mode)); 55962306a36Sopenharmony_ci if (ret < 0) 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * Source data hold time, default 0x05 = 2.5us 56462306a36Sopenharmony_ci * 0x00..0x3F = 0 .. 31.5us in steps of 0.5us 56562306a36Sopenharmony_ci * 0x0A = 5us 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci val = 0x0A; 56862306a36Sopenharmony_ci ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDHDTCTR, &val, 56962306a36Sopenharmony_ci sizeof(val)); 57062306a36Sopenharmony_ci if (ret < 0) 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* EQ control for gate signals, 0x00 = 0 us */ 57462306a36Sopenharmony_ci gseqctr[0] = 0x00; 57562306a36Sopenharmony_ci gseqctr[1] = 0x00; 57662306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_GSEQCTR, 57762306a36Sopenharmony_ci NT35510_P0_GSEQCTR_LEN, 57862306a36Sopenharmony_ci gseqctr); 57962306a36Sopenharmony_ci if (ret) 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_SDEQCTR, 58362306a36Sopenharmony_ci NT35510_P0_SDEQCTR_LEN, 58462306a36Sopenharmony_ci conf->sdeqctr); 58562306a36Sopenharmony_ci if (ret) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDVPCTR, 58962306a36Sopenharmony_ci &conf->sdvpctr, 1); 59062306a36Sopenharmony_ci if (ret < 0) 59162306a36Sopenharmony_ci return ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * Display timing control for active and idle off mode: 59562306a36Sopenharmony_ci * the first byte contains 59662306a36Sopenharmony_ci * the two high bits of T1A and second byte the low 8 bits, and 59762306a36Sopenharmony_ci * the valid range is 0x100 (257) to 0x3ff (1023) representing 59862306a36Sopenharmony_ci * 258..1024 (+1) pixel clock ticks for one scanline. At 20MHz pixel 59962306a36Sopenharmony_ci * clock this covers the range of 12.90us .. 51.20us in steps of 60062306a36Sopenharmony_ci * 0.05us, the default is 0x184 (388) representing 389 ticks. 60162306a36Sopenharmony_ci * The third byte is VBPDA, vertical back porch display active 60262306a36Sopenharmony_ci * and the fourth VFPDA, vertical front porch display active, 60362306a36Sopenharmony_ci * both given in number of scanlines in the range 0x02..0xff 60462306a36Sopenharmony_ci * for 2..255 scanlines. The fifth byte is 2 bits selecting 60562306a36Sopenharmony_ci * PSEL for active and idle off mode, how much the 20MHz clock 60662306a36Sopenharmony_ci * is divided by 0..3. This needs to be adjusted to get the right 60762306a36Sopenharmony_ci * frame rate. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci dpfrctr[0] = (conf->t1 >> 8) & 0xFF; 61062306a36Sopenharmony_ci dpfrctr[1] = conf->t1 & 0xFF; 61162306a36Sopenharmony_ci /* Vertical back porch */ 61262306a36Sopenharmony_ci dpfrctr[2] = conf->vbp; 61362306a36Sopenharmony_ci /* Vertical front porch */ 61462306a36Sopenharmony_ci dpfrctr[3] = conf->vfp; 61562306a36Sopenharmony_ci dpfrctr[4] = conf->psel; 61662306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR1, 61762306a36Sopenharmony_ci NT35510_P0_DPFRCTR1_LEN, 61862306a36Sopenharmony_ci dpfrctr); 61962306a36Sopenharmony_ci if (ret) 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci /* For idle and partial idle off mode we decrease front porch by one */ 62262306a36Sopenharmony_ci dpfrctr[3]--; 62362306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR2, 62462306a36Sopenharmony_ci NT35510_P0_DPFRCTR2_LEN, 62562306a36Sopenharmony_ci dpfrctr); 62662306a36Sopenharmony_ci if (ret) 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR3, 62962306a36Sopenharmony_ci NT35510_P0_DPFRCTR3_LEN, 63062306a36Sopenharmony_ci dpfrctr); 63162306a36Sopenharmony_ci if (ret) 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* Enable TE on vblank */ 63562306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 63662306a36Sopenharmony_ci if (ret) 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Turn on the pads? */ 64062306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P0_DPMCTR12, 64162306a36Sopenharmony_ci NT35510_P0_DPMCTR12_LEN, 64262306a36Sopenharmony_ci conf->dpmctr12); 64362306a36Sopenharmony_ci if (ret) 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic int nt35510_set_brightness(struct backlight_device *bl) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct nt35510 *nt = bl_get_data(bl); 65262306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 65362306a36Sopenharmony_ci u8 brightness = bl->props.brightness; 65462306a36Sopenharmony_ci int ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci dev_dbg(nt->dev, "set brightness %d\n", brightness); 65762306a36Sopenharmony_ci ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 65862306a36Sopenharmony_ci &brightness, 65962306a36Sopenharmony_ci sizeof(brightness)); 66062306a36Sopenharmony_ci if (ret < 0) 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic const struct backlight_ops nt35510_bl_ops = { 66762306a36Sopenharmony_ci .update_status = nt35510_set_brightness, 66862306a36Sopenharmony_ci}; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* 67162306a36Sopenharmony_ci * This power-on sequence 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_cistatic int nt35510_power_on(struct nt35510 *nt) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 67662306a36Sopenharmony_ci int ret; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(nt->supplies), nt->supplies); 67962306a36Sopenharmony_ci if (ret < 0) { 68062306a36Sopenharmony_ci dev_err(nt->dev, "unable to enable regulators\n"); 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Toggle RESET in accordance with datasheet page 370 */ 68562306a36Sopenharmony_ci if (nt->reset_gpio) { 68662306a36Sopenharmony_ci gpiod_set_value(nt->reset_gpio, 1); 68762306a36Sopenharmony_ci /* Active min 10 us according to datasheet, let's say 20 */ 68862306a36Sopenharmony_ci usleep_range(20, 1000); 68962306a36Sopenharmony_ci gpiod_set_value(nt->reset_gpio, 0); 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * 5 ms during sleep mode, 120 ms during sleep out mode 69262306a36Sopenharmony_ci * according to datasheet, let's use 120-140 ms. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci usleep_range(120000, 140000); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, MCS_CMD_MTP_READ_PARAM, 69862306a36Sopenharmony_ci ARRAY_SIZE(nt35510_mauc_mtp_read_param), 69962306a36Sopenharmony_ci nt35510_mauc_mtp_read_param); 70062306a36Sopenharmony_ci if (ret) 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, MCS_CMD_MTP_READ_SETTING, 70462306a36Sopenharmony_ci ARRAY_SIZE(nt35510_mauc_mtp_read_setting), 70562306a36Sopenharmony_ci nt35510_mauc_mtp_read_setting); 70662306a36Sopenharmony_ci if (ret) 70762306a36Sopenharmony_ci return ret; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci nt35510_read_id(nt); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Set up stuff in manufacturer control, page 1 */ 71262306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, MCS_CMD_MAUCCTR, 71362306a36Sopenharmony_ci ARRAY_SIZE(nt35510_mauc_select_page_1), 71462306a36Sopenharmony_ci nt35510_mauc_select_page_1); 71562306a36Sopenharmony_ci if (ret) 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = nt35510_setup_power(nt); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_POS, 72362306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 72462306a36Sopenharmony_ci nt->conf->gamma_corr_pos_r); 72562306a36Sopenharmony_ci if (ret) 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_POS, 72862306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 72962306a36Sopenharmony_ci nt->conf->gamma_corr_pos_g); 73062306a36Sopenharmony_ci if (ret) 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_POS, 73362306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 73462306a36Sopenharmony_ci nt->conf->gamma_corr_pos_b); 73562306a36Sopenharmony_ci if (ret) 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_NEG, 73862306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 73962306a36Sopenharmony_ci nt->conf->gamma_corr_neg_r); 74062306a36Sopenharmony_ci if (ret) 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_NEG, 74362306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 74462306a36Sopenharmony_ci nt->conf->gamma_corr_neg_g); 74562306a36Sopenharmony_ci if (ret) 74662306a36Sopenharmony_ci return ret; 74762306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_NEG, 74862306a36Sopenharmony_ci NT35510_P1_GAMMA_LEN, 74962306a36Sopenharmony_ci nt->conf->gamma_corr_neg_b); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Set up stuff in manufacturer control, page 0 */ 75462306a36Sopenharmony_ci ret = nt35510_send_long(nt, dsi, MCS_CMD_MAUCCTR, 75562306a36Sopenharmony_ci ARRAY_SIZE(nt35510_mauc_select_page_0), 75662306a36Sopenharmony_ci nt35510_mauc_select_page_0); 75762306a36Sopenharmony_ci if (ret) 75862306a36Sopenharmony_ci return ret; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci ret = nt35510_setup_display(nt); 76162306a36Sopenharmony_ci if (ret) 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int nt35510_power_off(struct nt35510 *nt) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int ret; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(nt->supplies), nt->supplies); 77262306a36Sopenharmony_ci if (ret) 77362306a36Sopenharmony_ci return ret; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (nt->reset_gpio) 77662306a36Sopenharmony_ci gpiod_set_value(nt->reset_gpio, 1); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int nt35510_unprepare(struct drm_panel *panel) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct nt35510 *nt = panel_to_nt35510(panel); 78462306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 78562306a36Sopenharmony_ci int ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 78862306a36Sopenharmony_ci if (ret) { 78962306a36Sopenharmony_ci dev_err(nt->dev, "failed to turn display off (%d)\n", ret); 79062306a36Sopenharmony_ci return ret; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci usleep_range(10000, 20000); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* Enter sleep mode */ 79562306a36Sopenharmony_ci ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 79662306a36Sopenharmony_ci if (ret) { 79762306a36Sopenharmony_ci dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret); 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Wait 4 frames, how much is that 5ms in the vendor driver */ 80262306a36Sopenharmony_ci usleep_range(5000, 10000); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ret = nt35510_power_off(nt); 80562306a36Sopenharmony_ci if (ret) 80662306a36Sopenharmony_ci return ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int nt35510_prepare(struct drm_panel *panel) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct nt35510 *nt = panel_to_nt35510(panel); 81462306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); 81562306a36Sopenharmony_ci int ret; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = nt35510_power_on(nt); 81862306a36Sopenharmony_ci if (ret) 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Exit sleep mode */ 82262306a36Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 82362306a36Sopenharmony_ci if (ret) { 82462306a36Sopenharmony_ci dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret); 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci /* Up to 120 ms */ 82862306a36Sopenharmony_ci usleep_range(120000, 150000); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 83162306a36Sopenharmony_ci if (ret) { 83262306a36Sopenharmony_ci dev_err(nt->dev, "failed to turn display on (%d)\n", ret); 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci /* Some 10 ms */ 83662306a36Sopenharmony_ci usleep_range(10000, 20000); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int nt35510_get_modes(struct drm_panel *panel, 84262306a36Sopenharmony_ci struct drm_connector *connector) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct nt35510 *nt = panel_to_nt35510(panel); 84562306a36Sopenharmony_ci struct drm_display_mode *mode; 84662306a36Sopenharmony_ci struct drm_display_info *info; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci info = &connector->display_info; 84962306a36Sopenharmony_ci info->width_mm = nt->conf->width_mm; 85062306a36Sopenharmony_ci info->height_mm = nt->conf->height_mm; 85162306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &nt->conf->mode); 85262306a36Sopenharmony_ci if (!mode) { 85362306a36Sopenharmony_ci dev_err(panel->dev, "bad mode or failed to add mode\n"); 85462306a36Sopenharmony_ci return -EINVAL; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci drm_mode_set_name(mode); 85762306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci mode->width_mm = nt->conf->width_mm; 86062306a36Sopenharmony_ci mode->height_mm = nt->conf->height_mm; 86162306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return 1; /* Number of modes */ 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic const struct drm_panel_funcs nt35510_drm_funcs = { 86762306a36Sopenharmony_ci .unprepare = nt35510_unprepare, 86862306a36Sopenharmony_ci .prepare = nt35510_prepare, 86962306a36Sopenharmony_ci .get_modes = nt35510_get_modes, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int nt35510_probe(struct mipi_dsi_device *dsi) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct device *dev = &dsi->dev; 87562306a36Sopenharmony_ci struct nt35510 *nt; 87662306a36Sopenharmony_ci int ret; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci nt = devm_kzalloc(dev, sizeof(struct nt35510), GFP_KERNEL); 87962306a36Sopenharmony_ci if (!nt) 88062306a36Sopenharmony_ci return -ENOMEM; 88162306a36Sopenharmony_ci mipi_dsi_set_drvdata(dsi, nt); 88262306a36Sopenharmony_ci nt->dev = dev; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci dsi->lanes = 2; 88562306a36Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 88662306a36Sopenharmony_ci /* 88762306a36Sopenharmony_ci * Datasheet suggests max HS rate for NT35510 is 250 MHz 88862306a36Sopenharmony_ci * (period time 4ns, see figure 7.6.4 page 365) and max LP rate is 88962306a36Sopenharmony_ci * 20 MHz (period time 50ns, see figure 7.6.6. page 366). 89062306a36Sopenharmony_ci * However these frequencies appear in source code for the Hydis 89162306a36Sopenharmony_ci * HVA40WV1 panel and setting up the LP frequency makes the panel 89262306a36Sopenharmony_ci * not work. 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * TODO: if other panels prove to be closer to the datasheet, 89562306a36Sopenharmony_ci * maybe make this a per-panel config in struct nt35510_config? 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci dsi->hs_rate = 349440000; 89862306a36Sopenharmony_ci dsi->lp_rate = 9600000; 89962306a36Sopenharmony_ci dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * Every new incarnation of this display must have a unique 90362306a36Sopenharmony_ci * data entry for the system in this driver. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci nt->conf = of_device_get_match_data(dev); 90662306a36Sopenharmony_ci if (!nt->conf) { 90762306a36Sopenharmony_ci dev_err(dev, "missing device configuration\n"); 90862306a36Sopenharmony_ci return -ENODEV; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci nt->supplies[0].supply = "vdd"; /* 2.3-4.8 V */ 91262306a36Sopenharmony_ci nt->supplies[1].supply = "vddi"; /* 1.65-3.3V */ 91362306a36Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->supplies), 91462306a36Sopenharmony_ci nt->supplies); 91562306a36Sopenharmony_ci if (ret < 0) 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci ret = regulator_set_voltage(nt->supplies[0].consumer, 91862306a36Sopenharmony_ci 2300000, 4800000); 91962306a36Sopenharmony_ci if (ret) 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci ret = regulator_set_voltage(nt->supplies[1].consumer, 92262306a36Sopenharmony_ci 1650000, 3300000); 92362306a36Sopenharmony_ci if (ret) 92462306a36Sopenharmony_ci return ret; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci nt->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); 92762306a36Sopenharmony_ci if (IS_ERR(nt->reset_gpio)) { 92862306a36Sopenharmony_ci dev_err(dev, "error getting RESET GPIO\n"); 92962306a36Sopenharmony_ci return PTR_ERR(nt->reset_gpio); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci drm_panel_init(&nt->panel, dev, &nt35510_drm_funcs, 93362306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* 93662306a36Sopenharmony_ci * First, try to locate an external backlight (such as on GPIO) 93762306a36Sopenharmony_ci * if this fails, assume we will want to use the internal backlight 93862306a36Sopenharmony_ci * control. 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci ret = drm_panel_of_backlight(&nt->panel); 94162306a36Sopenharmony_ci if (ret) { 94262306a36Sopenharmony_ci dev_err(dev, "error getting external backlight %d\n", ret); 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci if (!nt->panel.backlight) { 94662306a36Sopenharmony_ci struct backlight_device *bl; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci bl = devm_backlight_device_register(dev, "nt35510", dev, nt, 94962306a36Sopenharmony_ci &nt35510_bl_ops, NULL); 95062306a36Sopenharmony_ci if (IS_ERR(bl)) { 95162306a36Sopenharmony_ci dev_err(dev, "failed to register backlight device\n"); 95262306a36Sopenharmony_ci return PTR_ERR(bl); 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci bl->props.max_brightness = 255; 95562306a36Sopenharmony_ci bl->props.brightness = 255; 95662306a36Sopenharmony_ci bl->props.power = FB_BLANK_POWERDOWN; 95762306a36Sopenharmony_ci nt->panel.backlight = bl; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci drm_panel_add(&nt->panel); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = mipi_dsi_attach(dsi); 96362306a36Sopenharmony_ci if (ret < 0) 96462306a36Sopenharmony_ci drm_panel_remove(&nt->panel); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void nt35510_remove(struct mipi_dsi_device *dsi) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci struct nt35510 *nt = mipi_dsi_get_drvdata(dsi); 97262306a36Sopenharmony_ci int ret; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci mipi_dsi_detach(dsi); 97562306a36Sopenharmony_ci /* Power off */ 97662306a36Sopenharmony_ci ret = nt35510_power_off(nt); 97762306a36Sopenharmony_ci if (ret) 97862306a36Sopenharmony_ci dev_err(&dsi->dev, "Failed to power off\n"); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci drm_panel_remove(&nt->panel); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* 98462306a36Sopenharmony_ci * These gamma correction values are 10bit tuples, so only bits 0 and 1 is 98562306a36Sopenharmony_ci * ever used in the first byte. They form a positive and negative gamma 98662306a36Sopenharmony_ci * correction curve for each color, values must be strictly higher for each 98762306a36Sopenharmony_ci * step on the curve. As can be seen these default curves goes from 0x0001 98862306a36Sopenharmony_ci * to 0x03FE. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci#define NT35510_GAMMA_POS_DEFAULT 0x00, 0x01, 0x00, 0x43, 0x00, \ 99162306a36Sopenharmony_ci 0x6B, 0x00, 0x87, 0x00, 0xA3, 0x00, 0xCE, 0x00, 0xF1, 0x01, \ 99262306a36Sopenharmony_ci 0x27, 0x01, 0x53, 0x01, 0x98, 0x01, 0xCE, 0x02, 0x22, 0x02, \ 99362306a36Sopenharmony_ci 0x83, 0x02, 0x78, 0x02, 0x9E, 0x02, 0xDD, 0x03, 0x00, 0x03, \ 99462306a36Sopenharmony_ci 0x2E, 0x03, 0x54, 0x03, 0x7F, 0x03, 0x95, 0x03, 0xB3, 0x03, \ 99562306a36Sopenharmony_ci 0xC2, 0x03, 0xE1, 0x03, 0xF1, 0x03, 0xFE 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci#define NT35510_GAMMA_NEG_DEFAULT 0x00, 0x01, 0x00, 0x43, 0x00, \ 99862306a36Sopenharmony_ci 0x6B, 0x00, 0x87, 0x00, 0xA3, 0x00, 0xCE, 0x00, 0xF1, 0x01, \ 99962306a36Sopenharmony_ci 0x27, 0x01, 0x53, 0x01, 0x98, 0x01, 0xCE, 0x02, 0x22, 0x02, \ 100062306a36Sopenharmony_ci 0x43, 0x02, 0x50, 0x02, 0x9E, 0x02, 0xDD, 0x03, 0x00, 0x03, \ 100162306a36Sopenharmony_ci 0x2E, 0x03, 0x54, 0x03, 0x7F, 0x03, 0x95, 0x03, 0xB3, 0x03, \ 100262306a36Sopenharmony_ci 0xC2, 0x03, 0xE1, 0x03, 0xF1, 0x03, 0xFE 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/* 100562306a36Sopenharmony_ci * The Hydis HVA40WV1 panel 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_cistatic const struct nt35510_config nt35510_hydis_hva40wv1 = { 100862306a36Sopenharmony_ci .width_mm = 52, 100962306a36Sopenharmony_ci .height_mm = 86, 101062306a36Sopenharmony_ci /** 101162306a36Sopenharmony_ci * As the Hydis panel is used in command mode, the porches etc 101262306a36Sopenharmony_ci * are settings programmed internally into the NT35510 controller 101362306a36Sopenharmony_ci * and generated toward the physical display. As the panel is not 101462306a36Sopenharmony_ci * used in video mode, these are not really exposed to the DSI 101562306a36Sopenharmony_ci * host. 101662306a36Sopenharmony_ci * 101762306a36Sopenharmony_ci * Display frame rate control: 101862306a36Sopenharmony_ci * Frame rate = (20 MHz / 1) / (389 * (7 + 50 + 800)) ~= 60 Hz 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ci .mode = { 102162306a36Sopenharmony_ci /* The internal pixel clock of the NT35510 is 20 MHz */ 102262306a36Sopenharmony_ci .clock = 20000, 102362306a36Sopenharmony_ci .hdisplay = 480, 102462306a36Sopenharmony_ci .hsync_start = 480 + 2, /* HFP = 2 */ 102562306a36Sopenharmony_ci .hsync_end = 480 + 2 + 0, /* HSync = 0 */ 102662306a36Sopenharmony_ci .htotal = 480 + 2 + 0 + 5, /* HFP = 5 */ 102762306a36Sopenharmony_ci .vdisplay = 800, 102862306a36Sopenharmony_ci .vsync_start = 800 + 2, /* VFP = 2 */ 102962306a36Sopenharmony_ci .vsync_end = 800 + 2 + 0, /* VSync = 0 */ 103062306a36Sopenharmony_ci .vtotal = 800 + 2 + 0 + 5, /* VBP = 5 */ 103162306a36Sopenharmony_ci .flags = 0, 103262306a36Sopenharmony_ci }, 103362306a36Sopenharmony_ci /* 0x09: AVDD = 5.6V */ 103462306a36Sopenharmony_ci .avdd = { 0x09, 0x09, 0x09 }, 103562306a36Sopenharmony_ci /* 0x34: PCK = Hsync/2, BTP = 2 x VDDB */ 103662306a36Sopenharmony_ci .bt1ctr = { 0x34, 0x34, 0x34 }, 103762306a36Sopenharmony_ci /* 0x09: AVEE = -5.6V */ 103862306a36Sopenharmony_ci .avee = { 0x09, 0x09, 0x09 }, 103962306a36Sopenharmony_ci /* 0x24: NCK = Hsync/2, BTN = -2 x VDDB */ 104062306a36Sopenharmony_ci .bt2ctr = { 0x24, 0x24, 0x24 }, 104162306a36Sopenharmony_ci /* 0x05 = 12V */ 104262306a36Sopenharmony_ci .vgh = { 0x05, 0x05, 0x05 }, 104362306a36Sopenharmony_ci /* 0x24: NCKA = Hsync/2, VGH = 2 x AVDD - AVEE */ 104462306a36Sopenharmony_ci .bt4ctr = { 0x24, 0x24, 0x24 }, 104562306a36Sopenharmony_ci /* 0x0B = -13V */ 104662306a36Sopenharmony_ci .vgl = { 0x0B, 0x0B, 0x0B }, 104762306a36Sopenharmony_ci /* 0x24: LCKA = Hsync, VGL = AVDD + VCL - AVDD */ 104862306a36Sopenharmony_ci .bt5ctr = { 0x24, 0x24, 0x24 }, 104962306a36Sopenharmony_ci /* VGMP: 0x0A3 = 5.0375V, VGSP = 0V */ 105062306a36Sopenharmony_ci .vgp = { 0x00, 0xA3, 0x00 }, 105162306a36Sopenharmony_ci /* VGMP: 0x0A3 = 5.0375V, VGSP = 0V */ 105262306a36Sopenharmony_ci .vgn = { 0x00, 0xA3, 0x00 }, 105362306a36Sopenharmony_ci /* SDEQCTR: source driver EQ mode 2, 2.5 us rise time on each step */ 105462306a36Sopenharmony_ci .sdeqctr = { 0x01, 0x05, 0x05, 0x05 }, 105562306a36Sopenharmony_ci /* SDVPCTR: Normal operation off color during v porch */ 105662306a36Sopenharmony_ci .sdvpctr = 0x01, 105762306a36Sopenharmony_ci /* T1: number of pixel clocks on one scanline: 0x184 = 389 clocks */ 105862306a36Sopenharmony_ci .t1 = 0x0184, 105962306a36Sopenharmony_ci /* VBP: vertical back porch toward the panel */ 106062306a36Sopenharmony_ci .vbp = 7, 106162306a36Sopenharmony_ci /* VFP: vertical front porch toward the panel */ 106262306a36Sopenharmony_ci .vfp = 50, 106362306a36Sopenharmony_ci /* PSEL: divide pixel clock 20MHz with 1 (no clock downscaling) */ 106462306a36Sopenharmony_ci .psel = 0, 106562306a36Sopenharmony_ci /* DPTMCTR12: 0x03: LVGL = VGLX, overlap mode, swap R->L O->E */ 106662306a36Sopenharmony_ci .dpmctr12 = { 0x03, 0x00, 0x00, }, 106762306a36Sopenharmony_ci /* Default gamma correction values */ 106862306a36Sopenharmony_ci .gamma_corr_pos_r = { NT35510_GAMMA_POS_DEFAULT }, 106962306a36Sopenharmony_ci .gamma_corr_pos_g = { NT35510_GAMMA_POS_DEFAULT }, 107062306a36Sopenharmony_ci .gamma_corr_pos_b = { NT35510_GAMMA_POS_DEFAULT }, 107162306a36Sopenharmony_ci .gamma_corr_neg_r = { NT35510_GAMMA_NEG_DEFAULT }, 107262306a36Sopenharmony_ci .gamma_corr_neg_g = { NT35510_GAMMA_NEG_DEFAULT }, 107362306a36Sopenharmony_ci .gamma_corr_neg_b = { NT35510_GAMMA_NEG_DEFAULT }, 107462306a36Sopenharmony_ci}; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic const struct of_device_id nt35510_of_match[] = { 107762306a36Sopenharmony_ci { 107862306a36Sopenharmony_ci .compatible = "hydis,hva40wv1", 107962306a36Sopenharmony_ci .data = &nt35510_hydis_hva40wv1, 108062306a36Sopenharmony_ci }, 108162306a36Sopenharmony_ci { } 108262306a36Sopenharmony_ci}; 108362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nt35510_of_match); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic struct mipi_dsi_driver nt35510_driver = { 108662306a36Sopenharmony_ci .probe = nt35510_probe, 108762306a36Sopenharmony_ci .remove = nt35510_remove, 108862306a36Sopenharmony_ci .driver = { 108962306a36Sopenharmony_ci .name = "panel-novatek-nt35510", 109062306a36Sopenharmony_ci .of_match_table = nt35510_of_match, 109162306a36Sopenharmony_ci }, 109262306a36Sopenharmony_ci}; 109362306a36Sopenharmony_cimodule_mipi_dsi_driver(nt35510_driver); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 109662306a36Sopenharmony_ciMODULE_DESCRIPTION("NT35510-based panel driver"); 109762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1098