// SPDX-License-Identifier: GPL-2.0-only /* * Novatek NT35510 panel driver * Copyright (C) 2020 Linus Walleij <linus.walleij@linaro.org> * Based on code by Robert Teather (C) 2012 Samsung * * This display driver (and I refer to the physical component NT35510, * not this Linux kernel software driver) can handle: * 480x864, 480x854, 480x800, 480x720 and 480x640 pixel displays. * It has 480x840x24bit SRAM embedded for storing a frame. * When powered on the display is by default in 480x800 mode. * * The actual panels using this component have different names, but * the code needed to set up and configure the panel will be similar, * so they should all use the NT35510 driver with appropriate configuration * per-panel, e.g. for physical size. * * This driver is for the DSI interface to panels using the NT35510. * * The NT35510 can also use an RGB (DPI) interface combined with an * I2C or SPI interface for setting up the NT35510. If this is needed * this panel driver should be refactored to also support that use * case.
*/ #include <linux/backlight.h> #include <linux/bitops.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h>
/** * struct nt35510_config - the display-specific NT35510 configuration * * Some of the settings provide an array of bytes, A, B C which mean: * A = normal / idle off mode * B = idle on mode * C = partial / idle off mode * * Gamma correction arrays are 10bit numbers, two consecutive bytes * makes out one point on the gamma correction curve. The points are * not linearly placed along the X axis, we get points 0, 1, 3, 5 * 7, 11, 15, 23, 31, 47, 63, 95, 127, 128, 160, 192, 208, 224, 232, * 240, 244, 248, 250, 252, 254, 255. The voltages tuples form * V0, V1, V3 ... V255, with 0x0000 being the lowest voltage and * 0x03FF being the highest voltage. * * Each value must be strictly higher than the previous value forming * a rising curve like this: * * ^ * | V255 * | V254 * | .... * | V5 * | V3 * | V1 * | V0 * +-------------------------------------------> * * The details about all settings can be found in the NT35510 Application * Note.
*/ struct nt35510_config { /** * @width_mm: physical panel width [mm]
*/
u32 width_mm; /** * @height_mm: physical panel height [mm]
*/
u32 height_mm; /** * @mode: the display mode. This is only relevant outside the panel * in video mode: in command mode this is configuring the internal * timing in the display controller.
*/ conststruct drm_display_mode mode; /** * @mode_flags: DSI operation mode related flags
*/ unsignedlong mode_flags; /** * @cmds: enable DSI commands
*/
u32 cmds; /** * @avdd: setting for AVDD ranging from 0x00 = 6.5V to 0x14 = 4.5V * in 0.1V steps the default is 0x05 which means 6.0V
*/
u8 avdd[NT35510_P1_AVDD_LEN]; /** * @bt1ctr: setting for boost power control for the AVDD step-up * circuit (1) * bits 0..2 in the lower nibble controls PCK, the booster clock * frequency for the step-up circuit: * 0 = Hsync/32 * 1 = Hsync/16 * 2 = Hsync/8 * 3 = Hsync/4 * 4 = Hsync/2 * 5 = Hsync * 6 = Hsync x 2 * 7 = Hsync x 4 * bits 4..6 in the upper nibble controls BTP, the boosting * amplification for the step-up circuit: * 0 = Disable * 1 = 1.5 x VDDB * 2 = 1.66 x VDDB * 3 = 2 x VDDB * 4 = 2.5 x VDDB * 5 = 3 x VDDB * The defaults are 4 and 4 yielding 0x44
*/
u8 bt1ctr[NT35510_P1_BT1CTR_LEN]; /** * @avee: setting for AVEE ranging from 0x00 = -6.5V to 0x14 = -4.5V * in 0.1V steps the default is 0x05 which means -6.0V
*/
u8 avee[NT35510_P1_AVEE_LEN]; /** * @bt2ctr: setting for boost power control for the AVEE step-up * circuit (2) * bits 0..2 in the lower nibble controls NCK, the booster clock * frequency, the values are the same as for PCK in @bt1ctr. * bits 4..5 in the upper nibble controls BTN, the boosting * amplification for the step-up circuit. * 0 = Disable * 1 = -1.5 x VDDB * 2 = -2 x VDDB * 3 = -2.5 x VDDB * 4 = -3 x VDDB * The defaults are 4 and 3 yielding 0x34
*/
u8 bt2ctr[NT35510_P1_BT2CTR_LEN]; /** * @vcl: setting for VCL ranging from 0x00 = -2.5V to 0x11 = -4.0V * in 1V steps, the default is 0x00 which means -2.5V
*/
u8 vcl[NT35510_P1_VCL_LEN]; /** * @bt3ctr: setting for boost power control for the VCL step-up * circuit (3) * bits 0..2 in the lower nibble controls CLCK, the booster clock * frequency, the values are the same as for PCK in @bt1ctr. * bits 4..5 in the upper nibble controls BTCL, the boosting * amplification for the step-up circuit. * 0 = Disable * 1 = -0.5 x VDDB * 2 = -1 x VDDB * 3 = -2 x VDDB * The defaults are 4 and 2 yielding 0x24
*/
u8 bt3ctr[NT35510_P1_BT3CTR_LEN]; /** * @vgh: setting for VGH ranging from 0x00 = 7.0V to 0x0B = 18.0V * in 1V steps, the default is 0x08 which means 15V
*/
u8 vgh[NT35510_P1_VGH_LEN]; /** * @bt4ctr: setting for boost power control for the VGH step-up * circuit (4) * bits 0..2 in the lower nibble controls HCK, the booster clock * frequency, the values are the same as for PCK in @bt1ctr. * bits 4..5 in the upper nibble controls BTH, the boosting * amplification for the step-up circuit. * 0 = AVDD + VDDB * 1 = AVDD - AVEE * 2 = AVDD - AVEE + VDDB * 3 = AVDD x 2 - AVEE * The defaults are 4 and 3 yielding 0x34
*/
u8 bt4ctr[NT35510_P1_BT4CTR_LEN]; /** * @vgl: setting for VGL ranging from 0x00 = -2V to 0x0f = -15V in * 1V steps, the default is 0x08 which means -10V
*/
u8 vgl[NT35510_P1_VGL_LEN]; /** * @bt5ctr: setting for boost power control for the VGL step-up * circuit (5) * bits 0..2 in the lower nibble controls LCK, the booster clock * frequency, the values are the same as for PCK in @bt1ctr. * bits 4..5 in the upper nibble controls BTL, the boosting * amplification for the step-up circuit. * 0 = AVEE + VCL * 1 = AVEE - AVDD * 2 = AVEE + VCL - AVDD * 3 = AVEE x 2 - AVDD * The defaults are 3 and 2 yielding 0x32
*/
u8 bt5ctr[NT35510_P1_BT5CTR_LEN]; /** * @vgp: setting for VGP, the positive gamma divider voltages * VGMP the high voltage and VGSP the low voltage. * The first byte contains bit 8 of VGMP and VGSP in bits 4 and 0 * The second byte contains bit 0..7 of VGMP * The third byte contains bit 0..7 of VGSP * VGMP 0x00 = 3.0V .. 0x108 = 6.3V in steps of 12.5mV * VGSP 0x00 = 0V .. 0x111 = 3.7V in steps of 12.5mV
*/
u8 vgp[NT35510_P1_VGP_LEN]; /** * @vgn: setting for VGN, the negative gamma divider voltages, * same layout of bytes as @vgp.
*/
u8 vgn[NT35510_P1_VGN_LEN]; /** * @vcmoff: setting the DC VCOM offset voltage * The first byte contains bit 8 of VCM in bit 0 and VCMOFFSEL in bit 4. * The second byte contains bits 0..7 of VCM. * VCMOFFSEL the common voltage offset mode. * VCMOFFSEL 0x00 = VCOM .. 0x01 Gamma. * The default is 0x00. * VCM the VCOM output voltage (VCMOFFSEL = 0) or the internal register * offset for gamma voltage (VCMOFFSEL = 1). * VCM 0x00 = 0V/0 .. 0x118 = 3.5V/280 in steps of 12.5mV/1step * The default is 0x00 = 0V/0.
*/
u8 vcmoff[NT35510_P1_VCMOFF_LEN]; /** * @dopctr: setting optional control for display * ERR bits 0..1 in the first byte is the ERR pin output signal setting. * 0 = Disable, ERR pin output low * 1 = ERR pin output CRC error only * 2 = ERR pin output ECC error only * 3 = ERR pin output CRC and ECC error * The default is 0. * N565 bit 2 in the first byte is the 16-bit/pixel format selection. * 0 = R[4:0] + G[5:3] & G[2:0] + B[4:0] * 1 = G[2:0] + R[4:0] & B[4:0] + G[5:3] * The default is 0. * DIS_EoTP_HS bit 3 in the first byte is "DSI protocol violation" error * reporting. * 0 = reporting when error * 1 = not reporting when error * DSIM bit 4 in the first byte is the video mode data type enable * 0 = Video mode data type disable * 1 = Video mode data type enable * The default is 0. * DSIG bit 5 int the first byte is the generic r/w data type enable * 0 = Generic r/w disable * 1 = Generic r/w enable * The default is 0. * DSITE bit 6 in the first byte is TE line enable * 0 = TE line is disabled * 1 = TE line is enabled * The default is 0. * RAMKP bit 7 in the first byte is the frame memory keep/loss in * sleep-in mode * 0 = contents loss in sleep-in * 1 = contents keep in sleep-in * The default is 0. * CRL bit 1 in the second byte is the source driver data shift * direction selection. This bit is XOR operation with bit RSMX * of 3600h command. * 0 (RMSX = 0) = S1 -> S1440 * 0 (RMSX = 1) = S1440 -> S1 * 1 (RMSX = 0) = S1440 -> S1 * 1 (RMSX = 1) = S1 -> S1440 * The default is 0. * CTB bit 2 in the second byte is the vertical scanning direction * selection for gate control signals. This bit is XOR operation * with bit ML of 3600h command. * 0 (ML = 0) = Forward (top -> bottom) * 0 (ML = 1) = Reverse (bottom -> top) * 1 (ML = 0) = Reverse (bottom -> top) * 1 (ML = 1) = Forward (top -> bottom) * The default is 0. * CRGB bit 3 in the second byte is RGB-BGR order selection. This * bit is XOR operation with bit RGB of 3600h command. * 0 (RGB = 0) = RGB/Normal * 0 (RGB = 1) = BGR/RB swap * 1 (RGB = 0) = BGR/RB swap * 1 (RGB = 1) = RGB/Normal * The default is 0. * TE_PWR_SEL bit 4 in the second byte is the TE output voltage * level selection (only valid when DSTB_SEL = 0 or DSTB_SEL = 1, * VSEL = High and VDDI = 1.665~3.3V). * 0 = TE output voltage level is VDDI * 1 = TE output voltage level is VDDA * The default is 0.
*/
u8 dopctr[NT35510_P0_DOPCTR_LEN]; /** * @madctl: Memory data access control * RSMY bit 0 is flip vertical. Flips the display image top to down. * RSMX bit 1 is flip horizontal. Flips the display image left to right. * MH bit 2 is the horizontal refresh order. * RGB bit 3 is the RGB-BGR order. * 0 = RGB color sequence * 1 = BGR color sequence * ML bit 4 is the vertical refresh order. * MV bit 5 is the row/column exchange. * MX bit 6 is the column address order. * MY bit 7 is the row address order.
*/
u8 madctl; /** * @sdhdtctr: source output data hold time * 0x00..0x3F = 0..31.5us in steps of 0.5us * The default is 0x05 = 2.5us.
*/
u8 sdhdtctr; /** * @gseqctr: EQ control for gate signals * GFEQ_XX[3:0]: time setting of EQ step for falling edge in steps * of 0.5us. * The default is 0x07 = 3.5us * GREQ_XX[7:4]: time setting of EQ step for rising edge in steps * of 0.5us. * The default is 0x07 = 3.5us
*/
u8 gseqctr[NT35510_P0_GSEQCTR_LEN]; /** * @sdeqctr: Source driver control settings, first byte is * 0 for mode 1 and 1 for mode 2. Mode 1 uses two steps and * mode 2 uses three steps meaning EQS3 is not used in mode * 1. Mode 2 is default. The last three parameters are EQS1, EQS2 * and EQS3, setting the rise time for each equalizer step: * 0x00 = 0.0 us to 0x0f = 7.5 us in steps of 0.5us. The default * is 0x07 = 3.5 us.
*/
u8 sdeqctr[NT35510_P0_SDEQCTR_LEN]; /** * @sdvpctr: power/voltage behaviour during vertical porch time
*/
u8 sdvpctr; /** * @t1: the number of pixel clocks on one scanline, range * 0x100 (258 ticks) .. 0x3FF (1024 ticks) so the value + 1 * clock ticks.
*/
u16 t1; /** * @vbp: vertical back porch toward the PANEL note: not toward * the DSI host; these are separate interfaces, in from DSI host * and out to the panel.
*/
u8 vbp; /** * @vfp: vertical front porch toward the PANEL.
*/
u8 vfp; /** * @psel: pixel clock divisor: 0 = 1, 1 = 2, 2 = 4, 3 = 8.
*/
u8 psel; /** * @dpmctr12: Display timing control 12 * Byte 1 bit 4 selects LVGL voltage level: 0 = VGLX, 1 = VGL_REG * Byte 1 bit 1 selects gate signal mode: 0 = non-overlap, 1 = overlap * Byte 1 bit 0 selects output signal control R/L swap, 0 = normal * 1 = swap all O->E, L->R * Byte 2 is CLW delay clock for CK O/E and CKB O/E signals: * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps * Byte 3 is FTI_H0 delay time for STP O/E signals: * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps
*/
u8 dpmctr12[NT35510_P0_DPMCTR12_LEN]; /** * @gamma_corr_pos_r: Red gamma correction parameters, positive
*/
u8 gamma_corr_pos_r[NT35510_P1_GAMMA_LEN]; /** * @gamma_corr_pos_g: Green gamma correction parameters, positive
*/
u8 gamma_corr_pos_g[NT35510_P1_GAMMA_LEN]; /** * @gamma_corr_pos_b: Blue gamma correction parameters, positive
*/
u8 gamma_corr_pos_b[NT35510_P1_GAMMA_LEN]; /** * @gamma_corr_neg_r: Red gamma correction parameters, negative
*/
u8 gamma_corr_neg_r[NT35510_P1_GAMMA_LEN]; /** * @gamma_corr_neg_g: Green gamma correction parameters, negative
*/
u8 gamma_corr_neg_g[NT35510_P1_GAMMA_LEN]; /** * @gamma_corr_neg_b: Blue gamma correction parameters, negative
*/
u8 gamma_corr_neg_b[NT35510_P1_GAMMA_LEN]; /** * @wrdisbv: write display brightness * 0x00 value means the lowest brightness and 0xff value means * the highest brightness. * The default is 0x00.
*/
u8 wrdisbv; /** * @wrctrld: write control display * G bit 0 selects gamma curve: 0 = Manual, 1 = Automatic * DB bit 1 selects display brightness: 0 = Manual, 1 = Automatic * BL bit 2 controls backlight control: 0 = Off, 1 = On * DD bit 3 controls display dimming: 0 = Off, 1 = On * A bit 4 controls LABC block: 0 = Off, 1 = On * BCTRL bit 5 controls brightness block: 0 = Off, 1 = On
*/
u8 wrctrld; /** * @wrcabc: write content adaptive brightness control * There is possible to use 4 different modes for content adaptive * image functionality: * 0: Off * 1: User Interface Image (UI-Mode) * 2: Still Picture Image (Still-Mode) * 3: Moving Picture Image (Moving-Mode) * The default is 0
*/
u8 wrcabc; /** * @wrcabcmb: write CABC minimum brightness * Set the minimum brightness value of the display for CABC * function. * 0x00 value means the lowest brightness for CABC and 0xff * value means the highest brightness for CABC. * The default is 0x00.
*/
u8 wrcabcmb;
};
/** * struct nt35510 - state container for the NT35510 panel
*/ struct nt35510 { /** * @dev: the container device
*/ struct device *dev; /** * @conf: the specific panel configuration, as the NT35510 * can be combined with many physical panels, they can have * different physical dimensions and gamma correction etc, * so this is stored in the config.
*/ conststruct nt35510_config *conf; /** * @panel: the DRM panel object for the instance
*/ struct drm_panel panel; /** * @supplies: regulators supplying the panel
*/ struct regulator_bulk_data supplies[2]; /** * @reset_gpio: the reset line
*/ struct gpio_desc *reset_gpio;
};
ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID1, &id1, 1); if (ret < 0) {
dev_err(nt->dev, "could not read MTP ID1\n"); return ret;
}
ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID2, &id2, 1); if (ret < 0) {
dev_err(nt->dev, "could not read MTP ID2\n"); return ret;
}
ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID3, &id3, 1); if (ret < 0) {
dev_err(nt->dev, "could not read MTP ID3\n"); return ret;
}
/* * Multi-Time Programmable (?) memory contains manufacturer * ID (e.g. Hydis 0x55), driver ID (e.g. NT35510 0xc0) and * version.
*/
dev_info(nt->dev, "MTP ID manufacturer: %02x version: %02x driver: %02x\n", id1, id2, id3);
return 0;
}
/** * nt35510_setup_power() - set up power config in page 1 * @nt: the display instance to set up
*/ staticint nt35510_setup_power(struct nt35510 *nt)
{ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); int ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETAVDD,
NT35510_P1_AVDD_LEN,
nt->conf->avdd); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT1CTR,
NT35510_P1_BT1CTR_LEN,
nt->conf->bt1ctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETAVEE,
NT35510_P1_AVEE_LEN,
nt->conf->avee); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT2CTR,
NT35510_P1_BT2CTR_LEN,
nt->conf->bt2ctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVCL,
NT35510_P1_VCL_LEN,
nt->conf->vcl); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT3CTR,
NT35510_P1_BT3CTR_LEN,
nt->conf->bt3ctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGH,
NT35510_P1_VGH_LEN,
nt->conf->vgh); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT4CTR,
NT35510_P1_BT4CTR_LEN,
nt->conf->bt4ctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_VGHCTR,
ARRAY_SIZE(nt35510_vgh_on),
nt35510_vgh_on); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGL,
NT35510_P1_VGL_LEN,
nt->conf->vgl); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT5CTR,
NT35510_P1_BT5CTR_LEN,
nt->conf->bt5ctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGP,
NT35510_P1_VGP_LEN,
nt->conf->vgp); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGN,
NT35510_P1_VGN_LEN,
nt->conf->vgn); if (ret) return ret;
if (nt->conf->cmds & NT35510_CMD_SETVCMOFF) {
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVCMOFF,
NT35510_P1_VCMOFF_LEN,
nt->conf->vcmoff); if (ret) return ret;
}
/* Typically 10 ms */
usleep_range(10000, 20000);
return 0;
}
/** * nt35510_setup_display() - set up display config in page 0 * @nt: the display instance to set up
*/ staticint nt35510_setup_display(struct nt35510 *nt)
{ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); conststruct nt35510_config *conf = nt->conf;
u8 dpfrctr[NT35510_P0_DPFRCTR1_LEN]; int ret;
ret = nt35510_send_long(nt, dsi, NT35510_P0_DOPCTR,
NT35510_P0_DOPCTR_LEN,
conf->dopctr); if (ret) return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &conf->madctl, sizeof(conf->madctl)); if (ret < 0) return ret;
ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDHDTCTR, &conf->sdhdtctr, sizeof(conf->sdhdtctr)); if (ret < 0) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P0_GSEQCTR,
NT35510_P0_GSEQCTR_LEN,
conf->gseqctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P0_SDEQCTR,
NT35510_P0_SDEQCTR_LEN,
conf->sdeqctr); if (ret) return ret;
ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDVPCTR,
&conf->sdvpctr, 1); if (ret < 0) return ret;
/* * Display timing control for active and idle off mode: * the first byte contains * the two high bits of T1A and second byte the low 8 bits, and * the valid range is 0x100 (257) to 0x3ff (1023) representing * 258..1024 (+1) pixel clock ticks for one scanline. At 20MHz pixel * clock this covers the range of 12.90us .. 51.20us in steps of * 0.05us, the default is 0x184 (388) representing 389 ticks. * The third byte is VBPDA, vertical back porch display active * and the fourth VFPDA, vertical front porch display active, * both given in number of scanlines in the range 0x02..0xff * for 2..255 scanlines. The fifth byte is 2 bits selecting * PSEL for active and idle off mode, how much the 20MHz clock * is divided by 0..3. This needs to be adjusted to get the right * frame rate.
*/
dpfrctr[0] = (conf->t1 >> 8) & 0xFF;
dpfrctr[1] = conf->t1 & 0xFF; /* Vertical back porch */
dpfrctr[2] = conf->vbp; /* Vertical front porch */
dpfrctr[3] = conf->vfp;
dpfrctr[4] = conf->psel;
ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR1,
NT35510_P0_DPFRCTR1_LEN,
dpfrctr); if (ret) return ret; /* For idle and partial idle off mode we decrease front porch by one */
dpfrctr[3]--;
ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR2,
NT35510_P0_DPFRCTR2_LEN,
dpfrctr); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P0_DPFRCTR3,
NT35510_P0_DPFRCTR3_LEN,
dpfrctr); if (ret) return ret;
/* Enable TE on vblank */
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); if (ret) return ret;
/* Turn on the pads? */
ret = nt35510_send_long(nt, dsi, NT35510_P0_DPMCTR12,
NT35510_P0_DPMCTR12_LEN,
conf->dpmctr12); if (ret) return ret;
/* * This power-on sequence
*/ staticint nt35510_power_on(struct nt35510 *nt)
{ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(nt->supplies), nt->supplies); if (ret < 0) {
dev_err(nt->dev, "unable to enable regulators\n"); return ret;
}
/* Toggle RESET in accordance with datasheet page 370 */ if (nt->reset_gpio) {
gpiod_set_value(nt->reset_gpio, 1); /* Active min 10 us according to datasheet, let's say 20 */
usleep_range(20, 1000);
gpiod_set_value(nt->reset_gpio, 0); /* * 5 ms during sleep mode, 120 ms during sleep out mode * according to datasheet, let's use 120-140 ms.
*/
usleep_range(120000, 140000);
}
ret = nt35510_send_long(nt, dsi, MCS_CMD_MTP_READ_PARAM,
ARRAY_SIZE(nt35510_mauc_mtp_read_param),
nt35510_mauc_mtp_read_param); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, MCS_CMD_MTP_READ_SETTING,
ARRAY_SIZE(nt35510_mauc_mtp_read_setting),
nt35510_mauc_mtp_read_setting); if (ret) return ret;
nt35510_read_id(nt);
/* Set up stuff in manufacturer control, page 1 */
ret = nt35510_send_long(nt, dsi, MCS_CMD_MAUCCTR,
ARRAY_SIZE(nt35510_mauc_select_page_1),
nt35510_mauc_select_page_1); if (ret) return ret;
ret = nt35510_setup_power(nt); if (ret) return ret;
if (nt->conf->cmds & NT35510_CMD_CORRECT_GAMMA) {
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_r); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_g); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_b); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_r); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_g); if (ret) return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_b); if (ret) return ret;
}
/* Set up stuff in manufacturer control, page 0 */
ret = nt35510_send_long(nt, dsi, MCS_CMD_MAUCCTR,
ARRAY_SIZE(nt35510_mauc_select_page_0),
nt35510_mauc_select_page_0); if (ret) return ret;
ret = nt35510_setup_display(nt); if (ret) return ret;
return 0;
}
staticint nt35510_power_off(struct nt35510 *nt)
{ int ret;
ret = regulator_bulk_disable(ARRAY_SIZE(nt->supplies), nt->supplies); if (ret) return ret;
if (nt->reset_gpio)
gpiod_set_value(nt->reset_gpio, 1);
ret = mipi_dsi_dcs_set_display_off(dsi); if (ret) {
dev_err(nt->dev, "failed to turn display off (%d)\n", ret); return ret;
}
usleep_range(10000, 20000);
/* Enter sleep mode */
ret = mipi_dsi_dcs_enter_sleep_mode(dsi); if (ret) {
dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret); return ret;
}
/* Wait 4 frames, how much is that 5ms in the vendor driver */
usleep_range(5000, 10000);
/* Exit sleep mode */
ret = mipi_dsi_dcs_exit_sleep_mode(dsi); if (ret) {
dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret); return ret;
} /* Up to 120 ms */
usleep_range(120000, 150000);
if (nt->conf->cmds & NT35510_CMD_CONTROL_DISPLAY) {
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
&nt->conf->wrctrld, sizeof(nt->conf->wrctrld)); if (ret < 0) return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
&nt->conf->wrcabc, sizeof(nt->conf->wrcabc)); if (ret < 0) return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS,
&nt->conf->wrcabcmb, sizeof(nt->conf->wrcabcmb)); if (ret < 0) return ret;
}
ret = mipi_dsi_dcs_set_display_on(dsi); if (ret) {
dev_err(nt->dev, "failed to turn display on (%d)\n", ret); return ret;
} /* Some 10 ms */
usleep_range(10000, 20000);
nt = devm_drm_panel_alloc(dev, struct nt35510, panel,
&nt35510_drm_funcs,
DRM_MODE_CONNECTOR_DSI); if (IS_ERR(nt)) return PTR_ERR(nt);
mipi_dsi_set_drvdata(dsi, nt);
nt->dev = dev;
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888; /* * Datasheet suggests max HS rate for NT35510 is 250 MHz * (period time 4ns, see figure 7.6.4 page 365) and max LP rate is * 20 MHz (period time 50ns, see figure 7.6.6. page 366). * However these frequencies appear in source code for the Hydis * HVA40WV1 panel and setting up the LP frequency makes the panel * not work. * * TODO: if other panels prove to be closer to the datasheet, * maybe make this a per-panel config in struct nt35510_config?
*/
dsi->hs_rate = 349440000;
dsi->lp_rate = 9600000;
/* * Every new incarnation of this display must have a unique * data entry for the system in this driver.
*/
nt->conf = of_device_get_match_data(dev); if (!nt->conf) {
dev_err(dev, "missing device configuration\n"); return -ENODEV;
}
dsi->mode_flags = nt->conf->mode_flags;
nt->supplies[0].supply = "vdd"; /* 2.3-4.8 V */
nt->supplies[1].supply = "vddi"; /* 1.65-3.3V */
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->supplies),
nt->supplies); if (ret < 0) return ret;
ret = regulator_set_voltage(nt->supplies[0].consumer,
2300000, 4800000); if (ret) return ret;
ret = regulator_set_voltage(nt->supplies[1].consumer,
1650000, 3300000); if (ret) return ret;
/* * First, try to locate an external backlight (such as on GPIO) * if this fails, assume we will want to use the internal backlight * control.
*/
ret = drm_panel_of_backlight(&nt->panel); if (ret) {
dev_err(dev, "error getting external backlight %d\n", ret); return ret;
} if (!nt->panel.backlight) { struct backlight_device *bl;
mipi_dsi_detach(dsi); /* Power off */
ret = nt35510_power_off(nt); if (ret)
dev_err(&dsi->dev, "Failed to power off\n");
drm_panel_remove(&nt->panel);
}
/* * These gamma correction values are 10bit tuples, so only bits 0 and 1 is * ever used in the first byte. They form a positive and negative gamma * correction curve for each color, values must be strictly higher for each * step on the curve. As can be seen these default curves goes from 0x0001 * to 0x03FE.
*/ #define NT35510_GAMMA_POS_DEFAULT 0x00, 0x01, 0x00, 0x43, 0x00, \
0x6B, 0x00, 0x87, 0x00, 0xA3, 0x00, 0xCE, 0x00, 0xF1, 0x01, \
0x27, 0x01, 0x53, 0x01, 0x98, 0x01, 0xCE, 0x02, 0x22, 0x02, \
0x83, 0x02, 0x78, 0x02, 0x9E, 0x02, 0xDD, 0x03, 0x00, 0x03, \
0x2E, 0x03, 0x54, 0x03, 0x7F, 0x03, 0x95, 0x03, 0xB3, 0x03, \
0xC2, 0x03, 0xE1, 0x03, 0xF1, 0x03, 0xFE
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.