// SPDX-License-Identifier: GPL-2.0
/*
* NewVision NV3052C IPS LCD panel driver
*
* Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>
* Copyright (C) 2022, Christophe Branchereau <cbranchereau@gmail.com>
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <video/mipi_display.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
struct nv3052c_reg {
u8 cmd;
u8 val;
};
struct nv3052c_panel_info {
const struct drm_display_mode *display_modes;
unsigned int num_modes;
u16 width_mm, height_mm;
u32 bus_format, bus_flags;
const struct nv3052c_reg *panel_regs;
unsigned int panel_regs_len;
};
struct nv3052c {
struct device *dev;
struct drm_panel panel;
struct mipi_dbi dbi;
const struct nv3052c_panel_info *panel_info;
struct regulator *supply;
struct gpio_desc *reset_gpio;
};
static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {
// EXTC Command set enable, select page 1
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x01 },
// Mostly unknown registers
{ 0 xe3, 0 x00 },
{ 0 x40, 0 x00 },
{ 0 x03, 0 x40 },
{ 0 x04, 0 x00 },
{ 0 x05, 0 x03 },
{ 0 x08, 0 x00 },
{ 0 x09, 0 x07 },
{ 0 x0a, 0 x01 },
{ 0 x0b, 0 x32 },
{ 0 x0c, 0 x32 },
{ 0 x0d, 0 x0b },
{ 0 x0e, 0 x00 },
{ 0 x23, 0 xa0 },
{ 0 x24, 0 x0c },
{ 0 x25, 0 x06 },
{ 0 x26, 0 x14 },
{ 0 x27, 0 x14 },
{ 0 x38, 0 xcc }, // VCOM_ADJ1
{ 0 x39, 0 xd7 }, // VCOM_ADJ2
{ 0 x3a, 0 x4a }, // VCOM_ADJ3
{ 0 x28, 0 x40 },
{ 0 x29, 0 x01 },
{ 0 x2a, 0 xdf },
{ 0 x49, 0 x3c },
{ 0 x91, 0 x77 }, // EXTPW_CTRL2
{ 0 x92, 0 x77 }, // EXTPW_CTRL3
{ 0 xa0, 0 x55 },
{ 0 xa1, 0 x50 },
{ 0 xa4, 0 x9c },
{ 0 xa7, 0 x02 },
{ 0 xa8, 0 x01 },
{ 0 xa9, 0 x01 },
{ 0 xaa, 0 xfc },
{ 0 xab, 0 x28 },
{ 0 xac, 0 x06 },
{ 0 xad, 0 x06 },
{ 0 xae, 0 x06 },
{ 0 xaf, 0 x03 },
{ 0 xb0, 0 x08 },
{ 0 xb1, 0 x26 },
{ 0 xb2, 0 x28 },
{ 0 xb3, 0 x28 },
{ 0 xb4, 0 x33 },
{ 0 xb5, 0 x08 },
{ 0 xb6, 0 x26 },
{ 0 xb7, 0 x08 },
{ 0 xb8, 0 x26 },
{ 0 xf0, 0 x00 },
{ 0 xf6, 0 xc0 },
// EXTC Command set enable, select page 2
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Set gray scale voltage to adjust gamma
{ 0 xb0, 0 x0b }, // PGAMVR0
{ 0 xb1, 0 x16 }, // PGAMVR1
{ 0 xb2, 0 x17 }, // PGAMVR2
{ 0 xb3, 0 x2c }, // PGAMVR3
{ 0 xb4, 0 x32 }, // PGAMVR4
{ 0 xb5, 0 x3b }, // PGAMVR5
{ 0 xb6, 0 x29 }, // PGAMPR0
{ 0 xb7, 0 x40 }, // PGAMPR1
{ 0 xb8, 0 x0d }, // PGAMPK0
{ 0 xb9, 0 x05 }, // PGAMPK1
{ 0 xba, 0 x12 }, // PGAMPK2
{ 0 xbb, 0 x10 }, // PGAMPK3
{ 0 xbc, 0 x12 }, // PGAMPK4
{ 0 xbd, 0 x15 }, // PGAMPK5
{ 0 xbe, 0 x19 }, // PGAMPK6
{ 0 xbf, 0 x0e }, // PGAMPK7
{ 0 xc0, 0 x16 }, // PGAMPK8
{ 0 xc1, 0 x0a }, // PGAMPK9
// Set gray scale voltage to adjust gamma
{ 0 xd0, 0 x0c }, // NGAMVR0
{ 0 xd1, 0 x17 }, // NGAMVR0
{ 0 xd2, 0 x14 }, // NGAMVR1
{ 0 xd3, 0 x2e }, // NGAMVR2
{ 0 xd4, 0 x32 }, // NGAMVR3
{ 0 xd5, 0 x3c }, // NGAMVR4
{ 0 xd6, 0 x22 }, // NGAMPR0
{ 0 xd7, 0 x3d }, // NGAMPR1
{ 0 xd8, 0 x0d }, // NGAMPK0
{ 0 xd9, 0 x07 }, // NGAMPK1
{ 0 xda, 0 x13 }, // NGAMPK2
{ 0 xdb, 0 x13 }, // NGAMPK3
{ 0 xdc, 0 x11 }, // NGAMPK4
{ 0 xdd, 0 x15 }, // NGAMPK5
{ 0 xde, 0 x19 }, // NGAMPK6
{ 0 xdf, 0 x10 }, // NGAMPK7
{ 0 xe0, 0 x17 }, // NGAMPK8
{ 0 xe1, 0 x0a }, // NGAMPK9
// EXTC Command set enable, select page 3
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x03 },
// Set various timing settings
{ 0 x00, 0 x2a }, // GIP_VST_1
{ 0 x01, 0 x2a }, // GIP_VST_2
{ 0 x02, 0 x2a }, // GIP_VST_3
{ 0 x03, 0 x2a }, // GIP_VST_4
{ 0 x04, 0 x61 }, // GIP_VST_5
{ 0 x05, 0 x80 }, // GIP_VST_6
{ 0 x06, 0 xc7 }, // GIP_VST_7
{ 0 x07, 0 x01 }, // GIP_VST_8
{ 0 x08, 0 x03 }, // GIP_VST_9
{ 0 x09, 0 x04 }, // GIP_VST_10
{ 0 x70, 0 x22 }, // GIP_ECLK1
{ 0 x71, 0 x80 }, // GIP_ECLK2
{ 0 x30, 0 x2a }, // GIP_CLK_1
{ 0 x31, 0 x2a }, // GIP_CLK_2
{ 0 x32, 0 x2a }, // GIP_CLK_3
{ 0 x33, 0 x2a }, // GIP_CLK_4
{ 0 x34, 0 x61 }, // GIP_CLK_5
{ 0 x35, 0 xc5 }, // GIP_CLK_6
{ 0 x36, 0 x80 }, // GIP_CLK_7
{ 0 x37, 0 x23 }, // GIP_CLK_8
{ 0 x40, 0 x03 }, // GIP_CLKA_1
{ 0 x41, 0 x04 }, // GIP_CLKA_2
{ 0 x42, 0 x05 }, // GIP_CLKA_3
{ 0 x43, 0 x06 }, // GIP_CLKA_4
{ 0 x44, 0 x11 }, // GIP_CLKA_5
{ 0 x45, 0 xe8 }, // GIP_CLKA_6
{ 0 x46, 0 xe9 }, // GIP_CLKA_7
{ 0 x47, 0 x11 }, // GIP_CLKA_8
{ 0 x48, 0 xea }, // GIP_CLKA_9
{ 0 x49, 0 xeb }, // GIP_CLKA_10
{ 0 x50, 0 x07 }, // GIP_CLKB_1
{ 0 x51, 0 x08 }, // GIP_CLKB_2
{ 0 x52, 0 x09 }, // GIP_CLKB_3
{ 0 x53, 0 x0a }, // GIP_CLKB_4
{ 0 x54, 0 x11 }, // GIP_CLKB_5
{ 0 x55, 0 xec }, // GIP_CLKB_6
{ 0 x56, 0 xed }, // GIP_CLKB_7
{ 0 x57, 0 x11 }, // GIP_CLKB_8
{ 0 x58, 0 xef }, // GIP_CLKB_9
{ 0 x59, 0 xf0 }, // GIP_CLKB_10
// Map internal GOA signals to GOA output pad
{ 0 xb1, 0 x01 }, // PANELD2U2
{ 0 xb4, 0 x15 }, // PANELD2U5
{ 0 xb5, 0 x16 }, // PANELD2U6
{ 0 xb6, 0 x09 }, // PANELD2U7
{ 0 xb7, 0 x0f }, // PANELD2U8
{ 0 xb8, 0 x0d }, // PANELD2U9
{ 0 xb9, 0 x0b }, // PANELD2U10
{ 0 xba, 0 x00 }, // PANELD2U11
{ 0 xc7, 0 x02 }, // PANELD2U24
{ 0 xca, 0 x17 }, // PANELD2U27
{ 0 xcb, 0 x18 }, // PANELD2U28
{ 0 xcc, 0 x0a }, // PANELD2U29
{ 0 xcd, 0 x10 }, // PANELD2U30
{ 0 xce, 0 x0e }, // PANELD2U31
{ 0 xcf, 0 x0c }, // PANELD2U32
{ 0 xd0, 0 x00 }, // PANELD2U33
// Map internal GOA signals to GOA output pad
{ 0 x81, 0 x00 }, // PANELU2D2
{ 0 x84, 0 x15 }, // PANELU2D5
{ 0 x85, 0 x16 }, // PANELU2D6
{ 0 x86, 0 x10 }, // PANELU2D7
{ 0 x87, 0 x0a }, // PANELU2D8
{ 0 x88, 0 x0c }, // PANELU2D9
{ 0 x89, 0 x0e }, // PANELU2D10
{ 0 x8a, 0 x02 }, // PANELU2D11
{ 0 x97, 0 x00 }, // PANELU2D24
{ 0 x9a, 0 x17 }, // PANELU2D27
{ 0 x9b, 0 x18 }, // PANELU2D28
{ 0 x9c, 0 x0f }, // PANELU2D29
{ 0 x9d, 0 x09 }, // PANELU2D30
{ 0 x9e, 0 x0b }, // PANELU2D31
{ 0 x9f, 0 x0d }, // PANELU2D32
{ 0 xa0, 0 x01 }, // PANELU2D33
// EXTC Command set enable, select page 2
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Unknown registers
{ 0 x01, 0 x01 },
{ 0 x02, 0 xda },
{ 0 x03, 0 xba },
{ 0 x04, 0 xa8 },
{ 0 x05, 0 x9a },
{ 0 x06, 0 x70 },
{ 0 x07, 0 xff },
{ 0 x08, 0 x91 },
{ 0 x09, 0 x90 },
{ 0 x0a, 0 xff },
{ 0 x0b, 0 x8f },
{ 0 x0c, 0 x60 },
{ 0 x0d, 0 x58 },
{ 0 x0e, 0 x48 },
{ 0 x0f, 0 x38 },
{ 0 x10, 0 x2b },
// EXTC Command set enable, select page 0
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x00 },
// Display Access Control
{ 0 x36, 0 x0a }, // bgr = 1, ss = 1, gs = 0
};
static const struct nv3052c_reg fs035vg158_panel_regs[] = {
// EXTC Command set enable, select page 1
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x01 },
// Mostly unknown registers
{ 0 xe3, 0 x00 },
{ 0 x40, 0 x00 },
{ 0 x03, 0 x40 },
{ 0 x04, 0 x00 },
{ 0 x05, 0 x03 },
{ 0 x08, 0 x00 },
{ 0 x09, 0 x07 },
{ 0 x0a, 0 x01 },
{ 0 x0b, 0 x32 },
{ 0 x0c, 0 x32 },
{ 0 x0d, 0 x0b },
{ 0 x0e, 0 x00 },
{ 0 x23, 0 x20 }, // RGB interface control: DE MODE PCLK-N
{ 0 x24, 0 x0c },
{ 0 x25, 0 x06 },
{ 0 x26, 0 x14 },
{ 0 x27, 0 x14 },
{ 0 x38, 0 x9c }, //VCOM_ADJ1, different to ltk035c5444t
{ 0 x39, 0 xa7 }, //VCOM_ADJ2, different to ltk035c5444t
{ 0 x3a, 0 x50 }, //VCOM_ADJ3, different to ltk035c5444t
{ 0 x28, 0 x40 },
{ 0 x29, 0 x01 },
{ 0 x2a, 0 xdf },
{ 0 x49, 0 x3c },
{ 0 x91, 0 x57 }, //EXTPW_CTRL2, different to ltk035c5444t
{ 0 x92, 0 x57 }, //EXTPW_CTRL3, different to ltk035c5444t
{ 0 xa0, 0 x55 },
{ 0 xa1, 0 x50 },
{ 0 xa4, 0 x9c },
{ 0 xa7, 0 x02 },
{ 0 xa8, 0 x01 },
{ 0 xa9, 0 x01 },
{ 0 xaa, 0 xfc },
{ 0 xab, 0 x28 },
{ 0 xac, 0 x06 },
{ 0 xad, 0 x06 },
{ 0 xae, 0 x06 },
{ 0 xaf, 0 x03 },
{ 0 xb0, 0 x08 },
{ 0 xb1, 0 x26 },
{ 0 xb2, 0 x28 },
{ 0 xb3, 0 x28 },
{ 0 xb4, 0 x03 }, // Unknown, different to ltk035c5444
{ 0 xb5, 0 x08 },
{ 0 xb6, 0 x26 },
{ 0 xb7, 0 x08 },
{ 0 xb8, 0 x26 },
{ 0 xf0, 0 x00 },
{ 0 xf6, 0 xc0 },
// EXTC Command set enable, select page 0
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Set gray scale voltage to adjust gamma
{ 0 xb0, 0 x0b }, // PGAMVR0
{ 0 xb1, 0 x16 }, // PGAMVR1
{ 0 xb2, 0 x17 }, // PGAMVR2
{ 0 xb3, 0 x2c }, // PGAMVR3
{ 0 xb4, 0 x32 }, // PGAMVR4
{ 0 xb5, 0 x3b }, // PGAMVR5
{ 0 xb6, 0 x29 }, // PGAMPR0
{ 0 xb7, 0 x40 }, // PGAMPR1
{ 0 xb8, 0 x0d }, // PGAMPK0
{ 0 xb9, 0 x05 }, // PGAMPK1
{ 0 xba, 0 x12 }, // PGAMPK2
{ 0 xbb, 0 x10 }, // PGAMPK3
{ 0 xbc, 0 x12 }, // PGAMPK4
{ 0 xbd, 0 x15 }, // PGAMPK5
{ 0 xbe, 0 x19 }, // PGAMPK6
{ 0 xbf, 0 x0e }, // PGAMPK7
{ 0 xc0, 0 x16 }, // PGAMPK8
{ 0 xc1, 0 x0a }, // PGAMPK9
// Set gray scale voltage to adjust gamma
{ 0 xd0, 0 x0c }, // NGAMVR0
{ 0 xd1, 0 x17 }, // NGAMVR0
{ 0 xd2, 0 x14 }, // NGAMVR1
{ 0 xd3, 0 x2e }, // NGAMVR2
{ 0 xd4, 0 x32 }, // NGAMVR3
{ 0 xd5, 0 x3c }, // NGAMVR4
{ 0 xd6, 0 x22 }, // NGAMPR0
{ 0 xd7, 0 x3d }, // NGAMPR1
{ 0 xd8, 0 x0d }, // NGAMPK0
{ 0 xd9, 0 x07 }, // NGAMPK1
{ 0 xda, 0 x13 }, // NGAMPK2
{ 0 xdb, 0 x13 }, // NGAMPK3
{ 0 xdc, 0 x11 }, // NGAMPK4
{ 0 xdd, 0 x15 }, // NGAMPK5
{ 0 xde, 0 x19 }, // NGAMPK6
{ 0 xdf, 0 x10 }, // NGAMPK7
{ 0 xe0, 0 x17 }, // NGAMPK8
{ 0 xe1, 0 x0a }, // NGAMPK9
// EXTC Command set enable, select page 3
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x03 },
// Set various timing settings
{ 0 x00, 0 x2a }, // GIP_VST_1
{ 0 x01, 0 x2a }, // GIP_VST_2
{ 0 x02, 0 x2a }, // GIP_VST_3
{ 0 x03, 0 x2a }, // GIP_VST_4
{ 0 x04, 0 x61 }, // GIP_VST_5
{ 0 x05, 0 x80 }, // GIP_VST_6
{ 0 x06, 0 xc7 }, // GIP_VST_7
{ 0 x07, 0 x01 }, // GIP_VST_8
{ 0 x08, 0 x03 }, // GIP_VST_9
{ 0 x09, 0 x04 }, // GIP_VST_10
{ 0 x70, 0 x22 }, // GIP_ECLK1
{ 0 x71, 0 x80 }, // GIP_ECLK2
{ 0 x30, 0 x2a }, // GIP_CLK_1
{ 0 x31, 0 x2a }, // GIP_CLK_2
{ 0 x32, 0 x2a }, // GIP_CLK_3
{ 0 x33, 0 x2a }, // GIP_CLK_4
{ 0 x34, 0 x61 }, // GIP_CLK_5
{ 0 x35, 0 xc5 }, // GIP_CLK_6
{ 0 x36, 0 x80 }, // GIP_CLK_7
{ 0 x37, 0 x23 }, // GIP_CLK_8
{ 0 x40, 0 x03 }, // GIP_CLKA_1
{ 0 x41, 0 x04 }, // GIP_CLKA_2
{ 0 x42, 0 x05 }, // GIP_CLKA_3
{ 0 x43, 0 x06 }, // GIP_CLKA_4
{ 0 x44, 0 x11 }, // GIP_CLKA_5
{ 0 x45, 0 xe8 }, // GIP_CLKA_6
{ 0 x46, 0 xe9 }, // GIP_CLKA_7
{ 0 x47, 0 x11 }, // GIP_CLKA_8
{ 0 x48, 0 xea }, // GIP_CLKA_9
{ 0 x49, 0 xeb }, // GIP_CLKA_10
{ 0 x50, 0 x07 }, // GIP_CLKB_1
{ 0 x51, 0 x08 }, // GIP_CLKB_2
{ 0 x52, 0 x09 }, // GIP_CLKB_3
{ 0 x53, 0 x0a }, // GIP_CLKB_4
{ 0 x54, 0 x11 }, // GIP_CLKB_5
{ 0 x55, 0 xec }, // GIP_CLKB_6
{ 0 x56, 0 xed }, // GIP_CLKB_7
{ 0 x57, 0 x11 }, // GIP_CLKB_8
{ 0 x58, 0 xef }, // GIP_CLKB_9
{ 0 x59, 0 xf0 }, // GIP_CLKB_10
// Map internal GOA signals to GOA output pad
{ 0 xb1, 0 x01 }, // PANELD2U2
{ 0 xb4, 0 x15 }, // PANELD2U5
{ 0 xb5, 0 x16 }, // PANELD2U6
{ 0 xb6, 0 x09 }, // PANELD2U7
{ 0 xb7, 0 x0f }, // PANELD2U8
{ 0 xb8, 0 x0d }, // PANELD2U9
{ 0 xb9, 0 x0b }, // PANELD2U10
{ 0 xba, 0 x00 }, // PANELD2U11
{ 0 xc7, 0 x02 }, // PANELD2U24
{ 0 xca, 0 x17 }, // PANELD2U27
{ 0 xcb, 0 x18 }, // PANELD2U28
{ 0 xcc, 0 x0a }, // PANELD2U29
{ 0 xcd, 0 x10 }, // PANELD2U30
{ 0 xce, 0 x0e }, // PANELD2U31
{ 0 xcf, 0 x0c }, // PANELD2U32
{ 0 xd0, 0 x00 }, // PANELD2U33
// Map internal GOA signals to GOA output pad
{ 0 x81, 0 x00 }, // PANELU2D2
{ 0 x84, 0 x15 }, // PANELU2D5
{ 0 x85, 0 x16 }, // PANELU2D6
{ 0 x86, 0 x10 }, // PANELU2D7
{ 0 x87, 0 x0a }, // PANELU2D8
{ 0 x88, 0 x0c }, // PANELU2D9
{ 0 x89, 0 x0e }, // PANELU2D10
{ 0 x8a, 0 x02 }, // PANELU2D11
{ 0 x97, 0 x00 }, // PANELU2D24
{ 0 x9a, 0 x17 }, // PANELU2D27
{ 0 x9b, 0 x18 }, // PANELU2D28
{ 0 x9c, 0 x0f }, // PANELU2D29
{ 0 x9d, 0 x09 }, // PANELU2D30
{ 0 x9e, 0 x0b }, // PANELU2D31
{ 0 x9f, 0 x0d }, // PANELU2D32
{ 0 xa0, 0 x01 }, // PANELU2D33
// EXTC Command set enable, select page 2
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Unknown registers
{ 0 x01, 0 x01 },
{ 0 x02, 0 xda },
{ 0 x03, 0 xba },
{ 0 x04, 0 xa8 },
{ 0 x05, 0 x9a },
{ 0 x06, 0 x70 },
{ 0 x07, 0 xff },
{ 0 x08, 0 x91 },
{ 0 x09, 0 x90 },
{ 0 x0a, 0 xff },
{ 0 x0b, 0 x8f },
{ 0 x0c, 0 x60 },
{ 0 x0d, 0 x58 },
{ 0 x0e, 0 x48 },
{ 0 x0f, 0 x38 },
{ 0 x10, 0 x2b },
// EXTC Command set enable, select page 0
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x00 },
// Display Access Control
{ 0 x36, 0 x0a }, // bgr = 1, ss = 1, gs = 0
};
static const struct nv3052c_reg wl_355608_a8_panel_regs[] = {
// EXTC Command set enable, select page 1
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x01 },
// Mostly unknown registers
{ 0 xe3, 0 x00 },
{ 0 x40, 0 x00 },
{ 0 x03, 0 x40 },
{ 0 x04, 0 x00 },
{ 0 x05, 0 x03 },
{ 0 x08, 0 x00 },
{ 0 x09, 0 x07 },
{ 0 x0a, 0 x01 },
{ 0 x0b, 0 x32 },
{ 0 x0c, 0 x32 },
{ 0 x0d, 0 x0b },
{ 0 x0e, 0 x00 },
{ 0 x23, 0 xa0 },
{ 0 x24, 0 x0c },
{ 0 x25, 0 x06 },
{ 0 x26, 0 x14 },
{ 0 x27, 0 x14 },
{ 0 x38, 0 xcc }, // VCOM_ADJ1
{ 0 x39, 0 xd7 }, // VCOM_ADJ2
{ 0 x3a, 0 x44 }, // VCOM_ADJ3
{ 0 x28, 0 x40 },
{ 0 x29, 0 x01 },
{ 0 x2a, 0 xdf },
{ 0 x49, 0 x3c },
{ 0 x91, 0 x77 }, // EXTPW_CTRL2
{ 0 x92, 0 x77 }, // EXTPW_CTRL3
{ 0 xa0, 0 x55 },
{ 0 xa1, 0 x50 },
{ 0 xa4, 0 x9c },
{ 0 xa7, 0 x02 },
{ 0 xa8, 0 x01 },
{ 0 xa9, 0 x01 },
{ 0 xaa, 0 xfc },
{ 0 xab, 0 x28 },
{ 0 xac, 0 x06 },
{ 0 xad, 0 x06 },
{ 0 xae, 0 x06 },
{ 0 xaf, 0 x03 },
{ 0 xb0, 0 x08 },
{ 0 xb1, 0 x26 },
{ 0 xb2, 0 x28 },
{ 0 xb3, 0 x28 },
{ 0 xb4, 0 x33 },
{ 0 xb5, 0 x08 },
{ 0 xb6, 0 x26 },
{ 0 xb7, 0 x08 },
{ 0 xb8, 0 x26 },
{ 0 xf0, 0 x00 },
{ 0 xf6, 0 xc0 },
// EXTC Command set enable, select page 2
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Set gray scale voltage to adjust gamma
{ 0 xb0, 0 x0b }, // PGAMVR0
{ 0 xb1, 0 x16 }, // PGAMVR1
{ 0 xb2, 0 x17 }, // PGAMVR2
{ 0 xb3, 0 x2c }, // PGAMVR3
{ 0 xb4, 0 x32 }, // PGAMVR4
{ 0 xb5, 0 x3b }, // PGAMVR5
{ 0 xb6, 0 x29 }, // PGAMPR0
{ 0 xb7, 0 x40 }, // PGAMPR1
{ 0 xb8, 0 x0d }, // PGAMPK0
{ 0 xb9, 0 x05 }, // PGAMPK1
{ 0 xba, 0 x12 }, // PGAMPK2
{ 0 xbb, 0 x10 }, // PGAMPK3
{ 0 xbc, 0 x12 }, // PGAMPK4
{ 0 xbd, 0 x15 }, // PGAMPK5
{ 0 xbe, 0 x19 }, // PGAMPK6
{ 0 xbf, 0 x0e }, // PGAMPK7
{ 0 xc0, 0 x16 }, // PGAMPK8
{ 0 xc1, 0 x0a }, // PGAMPK9
// Set gray scale voltage to adjust gamma
{ 0 xd0, 0 x0c }, // NGAMVR0
{ 0 xd1, 0 x17 }, // NGAMVR0
{ 0 xd2, 0 x14 }, // NGAMVR1
{ 0 xd3, 0 x2e }, // NGAMVR2
{ 0 xd4, 0 x32 }, // NGAMVR3
{ 0 xd5, 0 x3c }, // NGAMVR4
{ 0 xd6, 0 x22 }, // NGAMPR0
{ 0 xd7, 0 x3d }, // NGAMPR1
{ 0 xd8, 0 x0d }, // NGAMPK0
{ 0 xd9, 0 x07 }, // NGAMPK1
{ 0 xda, 0 x13 }, // NGAMPK2
{ 0 xdb, 0 x13 }, // NGAMPK3
{ 0 xdc, 0 x11 }, // NGAMPK4
{ 0 xdd, 0 x15 }, // NGAMPK5
{ 0 xde, 0 x19 }, // NGAMPK6
{ 0 xdf, 0 x10 }, // NGAMPK7
{ 0 xe0, 0 x17 }, // NGAMPK8
{ 0 xe1, 0 x0a }, // NGAMPK9
// EXTC Command set enable, select page 3
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x03 },
// Set various timing settings
{ 0 x00, 0 x2a }, // GIP_VST_1
{ 0 x01, 0 x2a }, // GIP_VST_2
{ 0 x02, 0 x2a }, // GIP_VST_3
{ 0 x03, 0 x2a }, // GIP_VST_4
{ 0 x04, 0 x61 }, // GIP_VST_5
{ 0 x05, 0 x80 }, // GIP_VST_6
{ 0 x06, 0 xc7 }, // GIP_VST_7
{ 0 x07, 0 x01 }, // GIP_VST_8
{ 0 x08, 0 x03 }, // GIP_VST_9
{ 0 x09, 0 x04 }, // GIP_VST_10
{ 0 x70, 0 x22 }, // GIP_ECLK1
{ 0 x71, 0 x80 }, // GIP_ECLK2
{ 0 x30, 0 x2a }, // GIP_CLK_1
{ 0 x31, 0 x2a }, // GIP_CLK_2
{ 0 x32, 0 x2a }, // GIP_CLK_3
{ 0 x33, 0 x2a }, // GIP_CLK_4
{ 0 x34, 0 x61 }, // GIP_CLK_5
{ 0 x35, 0 xc5 }, // GIP_CLK_6
{ 0 x36, 0 x80 }, // GIP_CLK_7
{ 0 x37, 0 x23 }, // GIP_CLK_8
{ 0 x40, 0 x03 }, // GIP_CLKA_1
{ 0 x41, 0 x04 }, // GIP_CLKA_2
{ 0 x42, 0 x05 }, // GIP_CLKA_3
{ 0 x43, 0 x06 }, // GIP_CLKA_4
{ 0 x44, 0 x11 }, // GIP_CLKA_5
{ 0 x45, 0 xe8 }, // GIP_CLKA_6
{ 0 x46, 0 xe9 }, // GIP_CLKA_7
{ 0 x47, 0 x11 }, // GIP_CLKA_8
{ 0 x48, 0 xea }, // GIP_CLKA_9
{ 0 x49, 0 xeb }, // GIP_CLKA_10
{ 0 x50, 0 x07 }, // GIP_CLKB_1
{ 0 x51, 0 x08 }, // GIP_CLKB_2
{ 0 x52, 0 x09 }, // GIP_CLKB_3
{ 0 x53, 0 x0a }, // GIP_CLKB_4
{ 0 x54, 0 x11 }, // GIP_CLKB_5
{ 0 x55, 0 xec }, // GIP_CLKB_6
{ 0 x56, 0 xed }, // GIP_CLKB_7
{ 0 x57, 0 x11 }, // GIP_CLKB_8
{ 0 x58, 0 xef }, // GIP_CLKB_9
{ 0 x59, 0 xf0 }, // GIP_CLKB_10
// Map internal GOA signals to GOA output pad
{ 0 xb1, 0 x01 }, // PANELD2U2
{ 0 xb4, 0 x15 }, // PANELD2U5
{ 0 xb5, 0 x16 }, // PANELD2U6
{ 0 xb6, 0 x09 }, // PANELD2U7
{ 0 xb7, 0 x0f }, // PANELD2U8
{ 0 xb8, 0 x0d }, // PANELD2U9
{ 0 xb9, 0 x0b }, // PANELD2U10
{ 0 xba, 0 x00 }, // PANELD2U11
{ 0 xc7, 0 x02 }, // PANELD2U24
{ 0 xca, 0 x17 }, // PANELD2U27
{ 0 xcb, 0 x18 }, // PANELD2U28
{ 0 xcc, 0 x0a }, // PANELD2U29
{ 0 xcd, 0 x10 }, // PANELD2U30
{ 0 xce, 0 x0e }, // PANELD2U31
{ 0 xcf, 0 x0c }, // PANELD2U32
{ 0 xd0, 0 x00 }, // PANELD2U33
// Map internal GOA signals to GOA output pad
{ 0 x81, 0 x00 }, // PANELU2D2
{ 0 x84, 0 x15 }, // PANELU2D5
{ 0 x85, 0 x16 }, // PANELU2D6
{ 0 x86, 0 x10 }, // PANELU2D7
{ 0 x87, 0 x0a }, // PANELU2D8
{ 0 x88, 0 x0c }, // PANELU2D9
{ 0 x89, 0 x0e }, // PANELU2D10
{ 0 x8a, 0 x02 }, // PANELU2D11
{ 0 x97, 0 x00 }, // PANELU2D24
{ 0 x9a, 0 x17 }, // PANELU2D27
{ 0 x9b, 0 x18 }, // PANELU2D28
{ 0 x9c, 0 x0f }, // PANELU2D29
{ 0 x9d, 0 x09 }, // PANELU2D30
{ 0 x9e, 0 x0b }, // PANELU2D31
{ 0 x9f, 0 x0d }, // PANELU2D32
{ 0 xa0, 0 x01 }, // PANELU2D33
// EXTC Command set enable, select page 2
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x02 },
// Unknown registers
{ 0 x01, 0 x01 },
{ 0 x02, 0 xda },
{ 0 x03, 0 xba },
{ 0 x04, 0 xa8 },
{ 0 x05, 0 x9a },
{ 0 x06, 0 x70 },
{ 0 x07, 0 xff },
{ 0 x08, 0 x91 },
{ 0 x09, 0 x90 },
{ 0 x0a, 0 xff },
{ 0 x0b, 0 x8f },
{ 0 x0c, 0 x60 },
{ 0 x0d, 0 x58 },
{ 0 x0e, 0 x48 },
{ 0 x0f, 0 x38 },
{ 0 x10, 0 x2b },
// EXTC Command set enable, select page 0
{ 0 xff, 0 x30 }, { 0 xff, 0 x52 }, { 0 xff, 0 x00 },
// Display Access Control
{ 0 x36, 0 x0a }, // bgr = 1, ss = 1, gs = 0
};
static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
{
return container_of(panel, struct nv3052c, panel);
}
static int nv3052c_prepare(struct drm_panel *panel)
{
struct nv3052c *priv = to_nv3052c(panel);
const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs;
unsigned int panel_regs_len = priv->panel_info->panel_regs_len;
struct mipi_dbi *dbi = &priv->dbi;
unsigned int i;
int err;
err = regulator_enable(priv->supply);
if (err) {
dev_err(priv->dev, "Failed to enable power supply: %d\n" , err);
return err;
}
/* Reset the chip */
gpiod_set_value_cansleep(priv->reset_gpio, 1 );
usleep_range(10 , 1000 );
gpiod_set_value_cansleep(priv->reset_gpio, 0 );
usleep_range(5000 , 20000 );
for (i = 0 ; i < panel_regs_len; i++) {
err = mipi_dbi_command(dbi, panel_regs[i].cmd,
panel_regs[i].val);
if (err) {
dev_err(priv->dev, "Unable to set register: %d\n" , err);
goto err_disable_regulator;
}
}
err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
if (err) {
dev_err(priv->dev, "Unable to exit sleep mode: %d\n" , err);
goto err_disable_regulator;
}
return 0 ;
err_disable_regulator:
regulator_disable(priv->supply);
return err;
}
static int nv3052c_unprepare(struct drm_panel *panel)
{
struct nv3052c *priv = to_nv3052c(panel);
struct mipi_dbi *dbi = &priv->dbi;
int err;
err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
if (err)
dev_err(priv->dev, "Unable to enter sleep mode: %d\n" , err);
gpiod_set_value_cansleep(priv->reset_gpio, 1 );
regulator_disable(priv->supply);
return 0 ;
}
static int nv3052c_enable(struct drm_panel *panel)
{
struct nv3052c *priv = to_nv3052c(panel);
struct mipi_dbi *dbi = &priv->dbi;
int err;
err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
if (err) {
dev_err(priv->dev, "Unable to enable display: %d\n" , err);
return err;
}
if (panel->backlight) {
/* Wait for the picture to be ready before enabling backlight */
msleep(120 );
}
return 0 ;
}
static int nv3052c_disable(struct drm_panel *panel)
{
struct nv3052c *priv = to_nv3052c(panel);
struct mipi_dbi *dbi = &priv->dbi;
int err;
err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
if (err) {
dev_err(priv->dev, "Unable to disable display: %d\n" , err);
return err;
}
return 0 ;
}
static int nv3052c_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct nv3052c *priv = to_nv3052c(panel);
const struct nv3052c_panel_info *panel_info = priv->panel_info;
struct drm_display_mode *mode;
unsigned int i;
for (i = 0 ; i < panel_info->num_modes; i++) {
mode = drm_mode_duplicate(connector->dev,
&panel_info->display_modes[i]);
if (!mode)
return -ENOMEM;
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER;
if (panel_info->num_modes == 1 )
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
}
connector->display_info.bpc = 8 ;
connector->display_info.width_mm = panel_info->width_mm;
connector->display_info.height_mm = panel_info->height_mm;
drm_display_info_set_bus_formats(&connector->display_info,
&panel_info->bus_format, 1 );
connector->display_info.bus_flags = panel_info->bus_flags;
return panel_info->num_modes;
}
static const struct drm_panel_funcs nv3052c_funcs = {
.prepare = nv3052c_prepare,
.unprepare = nv3052c_unprepare,
.enable = nv3052c_enable,
.disable = nv3052c_disable,
.get_modes = nv3052c_get_modes,
};
static int nv3052c_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct nv3052c *priv;
int err;
priv = devm_drm_panel_alloc(dev, struct nv3052c, panel, &nv3052c_funcs,
DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(priv))
return PTR_ERR(priv);
priv->dev = dev;
priv->panel_info = of_device_get_match_data(dev);
if (!priv->panel_info)
return -EINVAL;
priv->supply = devm_regulator_get(dev, "power" );
if (IS_ERR(priv->supply))
return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n" );
priv->reset_gpio = devm_gpiod_get(dev, "reset" , GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset_gpio))
return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n" );
err = mipi_dbi_spi_init(spi, &priv->dbi, NULL);
if (err)
return dev_err_probe(dev, err, "MIPI DBI init failed\n" );
priv->dbi.read_commands = NULL;
spi_set_drvdata(spi, priv);
err = drm_panel_of_backlight(&priv->panel);
if (err)
return dev_err_probe(dev, err, "Failed to attach backlight\n" );
drm_panel_add(&priv->panel);
return 0 ;
}
static void nv3052c_remove(struct spi_device *spi)
{
struct nv3052c *priv = spi_get_drvdata(spi);
drm_panel_remove(&priv->panel);
drm_panel_disable(&priv->panel);
drm_panel_unprepare(&priv->panel);
}
static const struct drm_display_mode ltk035c5444t_modes[] = {
{ /* 60 Hz */
.clock = 24000 ,
.hdisplay = 640 ,
.hsync_start = 640 + 96 ,
.hsync_end = 640 + 96 + 16 ,
.htotal = 640 + 96 + 16 + 48 ,
.vdisplay = 480 ,
.vsync_start = 480 + 5 ,
.vsync_end = 480 + 5 + 2 ,
.vtotal = 480 + 5 + 2 + 13 ,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
},
{ /* 50 Hz */
.clock = 18000 ,
.hdisplay = 640 ,
.hsync_start = 640 + 39 ,
.hsync_end = 640 + 39 + 2 ,
.htotal = 640 + 39 + 2 + 39 ,
.vdisplay = 480 ,
.vsync_start = 480 + 5 ,
.vsync_end = 480 + 5 + 2 ,
.vtotal = 480 + 5 + 2 + 13 ,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
},
};
static const struct drm_display_mode fs035vg158_modes[] = {
{ /* 60 Hz */
.clock = 21000 ,
.hdisplay = 640 ,
.hsync_start = 640 + 34 ,
.hsync_end = 640 + 34 + 4 ,
.htotal = 640 + 34 + 4 + 20 ,
.vdisplay = 480 ,
.vsync_start = 480 + 12 ,
.vsync_end = 480 + 12 + 4 ,
.vtotal = 480 + 12 + 4 + 6 ,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
},
};
static const struct drm_display_mode wl_355608_a8_mode[] = {
{
.clock = 24000 ,
.hdisplay = 640 ,
.hsync_start = 640 + 64 ,
.hsync_end = 640 + 64 + 20 ,
.htotal = 640 + 64 + 20 + 46 ,
.vdisplay = 480 ,
.vsync_start = 480 + 21 ,
.vsync_end = 480 + 21 + 4 ,
.vtotal = 480 + 21 + 4 + 15 ,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
},
};
static const struct nv3052c_panel_info ltk035c5444t_panel_info = {
.display_modes = ltk035c5444t_modes,
.num_modes = ARRAY_SIZE(ltk035c5444t_modes),
.width_mm = 77 ,
.height_mm = 64 ,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
.panel_regs = ltk035c5444t_panel_regs,
.panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs),
};
static const struct nv3052c_panel_info fs035vg158_panel_info = {
.display_modes = fs035vg158_modes,
.num_modes = ARRAY_SIZE(fs035vg158_modes),
.width_mm = 70 ,
.height_mm = 53 ,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
.panel_regs = fs035vg158_panel_regs,
.panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs),
};
static const struct nv3052c_panel_info wl_355608_a8_panel_info = {
.display_modes = wl_355608_a8_mode,
.num_modes = ARRAY_SIZE(wl_355608_a8_mode),
.width_mm = 150 ,
.height_mm = 94 ,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
.panel_regs = wl_355608_a8_panel_regs,
.panel_regs_len = ARRAY_SIZE(wl_355608_a8_panel_regs),
};
static const struct spi_device_id nv3052c_ids[] = {
{ "ltk035c5444t" , },
{ "fs035vg158" , },
{ "rg35xx-plus-panel" , },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, nv3052c_ids);
static const struct of_device_id nv3052c_of_match[] = {
{ .compatible = "leadtek,ltk035c5444t" , .data = <k035c5444t_panel_info },
{ .compatible = "fascontek,fs035vg158" , .data = &fs035vg158_panel_info },
{ .compatible = "anbernic,rg35xx-plus-panel" , .data = &wl_355608_a8_panel_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nv3052c_of_match);
static struct spi_driver nv3052c_driver = {
.driver = {
.name = "nv3052c" ,
.of_match_table = nv3052c_of_match,
},
.id_table = nv3052c_ids,
.probe = nv3052c_probe,
.remove = nv3052c_remove,
};
module_spi_driver(nv3052c_driver);
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" );
MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>" );
MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com" );
MODULE_DESCRIPTION("NewVision NV3052C IPS LCD panel driver" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=91 H=95 G=92
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland