summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/panel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-12-14 11:07:56 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2020-12-14 11:07:56 -0800
commit1d36dffa5d887715dacca0f717f4519b7be5e498 (patch)
treea68f7c00dbb3036a67806ed6c6b8cc61c3cff60d /drivers/gpu/drm/panel
parent2c85ebc57b3e1817b6ce1a6b703928e113a90442 (diff)
parentb10733527bfd864605c33ab2e9a886eec317ec39 (diff)
downloadlinux-1d36dffa5d887715dacca0f717f4519b7be5e498.tar.bz2
Merge tag 'drm-next-2020-12-11' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "Not a huge amount of big things here, AMD has support for a few new HW variants (vangogh, green sardine, dimgrey cavefish), Intel has some more DG1 enablement. We have a few big reworks of the TTM layers and interfaces, GEM and atomic internal API reworks cross tree. fbdev is marked orphaned in here as well to reflect the current reality. core: - documentation updates - deprecate DRM_FORMAT_MOD_NONE - atomic crtc enable/disable rework - GEM convert drivers to gem object functions - remove SCATTER_LIST_MAX_SEGMENT sched: - avoid infinite waits ttm: - remove AGP support - don't modify caching for swapout - ttm pinning rework - major TTM reworks - new backend allocator - multihop support vram-helper: - top down BO placement fix - TTM changes - GEM object support displayport: - DP 2.0 DPCD prep work - DP MST extended DPCD caps fbdev: - mark as orphaned amdgpu: - Initial Vangogh support - Green Sardine support - Dimgrey Cavefish support - SG display support for renoir - SMU7 improvements - gfx9+ modiifier support - CI BACO fixes radeon: - expose voltage via hwmon on SUMO amdkfd: - fix unique id handling i915: - more DG1 enablement - bigjoiner support - integer scaling filter support - async flip support - ICL+ DSI command mode - Improve display shutdown - Display refactoring - eLLC machine fbdev loading fix - dma scatterlist fixes - TGL hang fixes - eLLC display buffer caching on SKL+ - MOCS PTE seeting for gen9+ msm: - Shutdown hook - GPU cooling device support - DSI 7nm and 10nm phy/pll updates - sm8150/sm2850 DPU support - GEM locking re-work - LLCC system cache support aspeed: - sysfs output config support ast: - LUT fix - new display mode gma500: - remove 2d framebuffer accel panfrost: - move gpu reset to a worker exynos: - new HDMI mode support mediatek: - MT8167 support - yaml bindings - MIPI DSI phy code moved etnaviv: - new perf counter - more lockdep annotation hibmc: - i2c DDC support ingenic: - pixel clock reset fix - reserved memory support - allow both DMA channels at once - different pixel format support - 30/24/8-bit palette modes tilcdc: - don't keep vblank irq enabled vc4: - new maintainer added - DSI registration fix virtio: - blob resource support - host visible and cross-device support - uuid api support" * tag 'drm-next-2020-12-11' of git://anongit.freedesktop.org/drm/drm: (1754 commits) drm/amdgpu: Initialise drm_gem_object_funcs for imported BOs drm/amdgpu: fix size calculation with stolen vga memory drm/amdgpu: remove amdgpu_ttm_late_init and amdgpu_bo_late_init drm/amdgpu: free the pre-OS console framebuffer after the first modeset drm/amdgpu: enable runtime pm using BACO on CI dGPUs drm/amdgpu/cik: enable BACO reset on Bonaire drm/amd/pm: update smu10.h WORKLOAD_PPLIB setting for raven drm/amd/pm: remove one unsupported smu function for vangogh drm/amd/display: setup system context for APUs drm/amd/display: add S/G support for Vangogh drm/amdkfd: Fix leak in dmabuf import drm/amdgpu: use AMDGPU_NUM_VMID when possible drm/amdgpu: fix sdma instance fw version and feature version init drm/amd/pm: update driver if version for dimgrey_cavefish drm/amd/display: 3.2.115 drm/amd/display: [FW Promotion] Release 0.0.45 drm/amd/display: Revert DCN2.1 dram_clock_change_latency update drm/amd/display: Enable gpu_vm_support for dcn3.01 drm/amd/display: Fixed the audio noise during mode switching with HDCP mode on drm/amd/display: Add wm table for Renoir ...
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r--drivers/gpu/drm/panel/Kconfig42
-rw-r--r--drivers/gpu/drm/panel/Makefile4
-rw-r--r--drivers/gpu/drm/panel/panel-abt-y030xx067a.c363
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9322.c2
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36672a.c711
-rw-r--r--drivers/gpu/drm/panel/panel-orisetech-otm8009a.c20
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm68200.c14
-rw-r--r--drivers/gpu/drm/panel/panel-ronbo-rb070d30.c7
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c40
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c9
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-sofef00.c351
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c37
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7703.c2
-rw-r--r--drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c250
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-td028ttec1.c7
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-tpg110.c3
17 files changed, 1807 insertions, 57 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index b9dbedf8f15e..b4e021ea30f9 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -8,6 +8,15 @@ config DRM_PANEL
menu "Display Panels"
depends on DRM && DRM_PANEL
+config DRM_PANEL_ABT_Y030XX067A
+ tristate "ABT Y030XX067A 320x480 LCD panel"
+ depends on OF && SPI
+ select REGMAP_SPI
+ help
+ Say Y here to enable support for the Asia Better Technology Ltd.
+ Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300
+ and RG-99 handheld gaming consoles.
+
config DRM_PANEL_ARM_VERSATILE
tristate "ARM Versatile panel driver"
depends on OF
@@ -208,6 +217,16 @@ config DRM_PANEL_NOVATEK_NT35510
around the Novatek NT35510 display controller, such as some
Hydis panels.
+config DRM_PANEL_NOVATEK_NT36672A
+ tristate "Novatek NT36672A DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the panels built
+ around the Novatek NT36672A display controller, such as some
+ Tianma panels used in a few Xiaomi Poco F1 mobile phones.
+
config DRM_PANEL_NOVATEK_NT39016
tristate "Novatek NT39016 RGB/SPI panel"
depends on OF && SPI
@@ -361,6 +380,18 @@ config DRM_PANEL_SAMSUNG_S6E8AA0
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
+config DRM_PANEL_SAMSUNG_SOFEF00
+ tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y or M here if you want to enable support for the Samsung AMOLED
+ command mode panels found in the OnePlus 6/6T smartphones.
+
+ The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively
+
config DRM_PANEL_SEIKO_43WVF1G
tristate "Seiko 43WVF1G panel"
depends on OF
@@ -450,6 +481,17 @@ config DRM_PANEL_SONY_ACX565AKM
Say Y here if you want to enable support for the Sony ACX565AKM
800x600 3.5" panel (found on the Nokia N900).
+config DRM_PANEL_TDO_TL070WSH30
+ tristate "TDO TL070WSH30 DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for TDO TL070WSH30 TFT-LCD
+ panel module. The panel has a 1024×600 resolution and uses
+ 24 bit RGB per pixel. It provides a MIPI DSI interface to
+ the host, a built-in LED backlight and touch controller.
+
config DRM_PANEL_TPO_TD028TTEC1
tristate "Toppoly (TPO) TD028TTEC1 panel driver"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 2ba560bca61d..ebbf488c7eac 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
@@ -38,6 +40,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
@@ -47,6 +50,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
diff --git a/drivers/gpu/drm/panel/panel-abt-y030xx067a.c b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c
new file mode 100644
index 000000000000..2d8794d495d0
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Asia Better Technology Ltd. Y030XX067A IPS LCD panel driver
+ *
+ * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2020, 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_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define REG00_VBRT_CTRL(val) (val)
+
+#define REG01_COM_DC(val) (val)
+
+#define REG02_DA_CONTRAST(val) (val)
+#define REG02_VESA_SEL(val) ((val) << 5)
+#define REG02_COMDC_SW BIT(7)
+
+#define REG03_VPOSITION(val) (val)
+#define REG03_BSMOUNT BIT(5)
+#define REG03_COMTST BIT(6)
+#define REG03_HPOSITION1 BIT(7)
+
+#define REG04_HPOSITION1(val) (val)
+
+#define REG05_CLIP BIT(0)
+#define REG05_NVM_VREFRESH BIT(1)
+#define REG05_SLFR BIT(2)
+#define REG05_SLBRCHARGE(val) ((val) << 3)
+#define REG05_PRECHARGE_LEVEL(val) ((val) << 6)
+
+#define REG06_TEST5 BIT(0)
+#define REG06_SLDWN BIT(1)
+#define REG06_SLRGT BIT(2)
+#define REG06_TEST2 BIT(3)
+#define REG06_XPSAVE BIT(4)
+#define REG06_GAMMA_SEL(val) ((val) << 5)
+#define REG06_NT BIT(7)
+
+#define REG07_TEST1 BIT(0)
+#define REG07_HDVD_POL BIT(1)
+#define REG07_CK_POL BIT(2)
+#define REG07_TEST3 BIT(3)
+#define REG07_TEST4 BIT(4)
+#define REG07_480_LINEMASK BIT(5)
+#define REG07_AMPTST(val) ((val) << 6)
+
+#define REG08_SLHRC(val) (val)
+#define REG08_CLOCK_DIV(val) ((val) << 2)
+#define REG08_PANEL(val) ((val) << 5)
+
+#define REG09_SUB_BRIGHT_R(val) (val)
+#define REG09_NW_NB BIT(6)
+#define REG09_IPCON BIT(7)
+
+#define REG0A_SUB_BRIGHT_B(val) (val)
+#define REG0A_PAIR BIT(6)
+#define REG0A_DE_SEL BIT(7)
+
+#define REG0B_MBK_POSITION(val) (val)
+#define REG0B_HD_FREERUN BIT(4)
+#define REG0B_VD_FREERUN BIT(5)
+#define REG0B_YUV2BIN(val) ((val) << 6)
+
+#define REG0C_CONTRAST_R(val) (val)
+#define REG0C_DOUBLEREAD BIT(7)
+
+#define REG0D_CONTRAST_G(val) (val)
+#define REG0D_RGB_YUV BIT(7)
+
+#define REG0E_CONTRAST_B(val) (val)
+#define REG0E_PIXELCOLORDRIVE BIT(7)
+
+#define REG0F_ASPECT BIT(0)
+#define REG0F_OVERSCAN(val) ((val) << 1)
+#define REG0F_FRAMEWIDTH(val) ((val) << 3)
+
+#define REG10_BRIGHT(val) (val)
+
+#define REG11_SIG_GAIN(val) (val)
+#define REG11_SIGC_CNTL BIT(6)
+#define REG11_SIGC_POL BIT(7)
+
+#define REG12_COLOR(val) (val)
+#define REG12_PWCKSEL(val) ((val) << 6)
+
+#define REG13_4096LEVEL_CNTL(val) (val)
+#define REG13_SL4096(val) ((val) << 4)
+#define REG13_LIMITER_CONTROL BIT(7)
+
+#define REG14_PANEL_TEST(val) (val)
+
+#define REG15_NVM_LINK0 BIT(0)
+#define REG15_NVM_LINK1 BIT(1)
+#define REG15_NVM_LINK2 BIT(2)
+#define REG15_NVM_LINK3 BIT(3)
+#define REG15_NVM_LINK4 BIT(4)
+#define REG15_NVM_LINK5 BIT(5)
+#define REG15_NVM_LINK6 BIT(6)
+#define REG15_NVM_LINK7 BIT(7)
+
+struct y030xx067a_info {
+ const struct drm_display_mode *display_modes;
+ unsigned int num_modes;
+ u16 width_mm, height_mm;
+ u32 bus_format, bus_flags;
+};
+
+struct y030xx067a {
+ struct drm_panel panel;
+ struct spi_device *spi;
+ struct regmap *map;
+
+ const struct y030xx067a_info *panel_info;
+
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct y030xx067a *to_y030xx067a(struct drm_panel *panel)
+{
+ return container_of(panel, struct y030xx067a, panel);
+}
+
+static const struct reg_sequence y030xx067a_init_sequence[] = {
+ { 0x00, REG00_VBRT_CTRL(0x7f) },
+ { 0x01, REG01_COM_DC(0x3c) },
+ { 0x02, REG02_VESA_SEL(0x3) | REG02_DA_CONTRAST(0x1f) },
+ { 0x03, REG03_VPOSITION(0x0a) },
+ { 0x04, REG04_HPOSITION1(0xd2) },
+ { 0x05, REG05_CLIP | REG05_NVM_VREFRESH | REG05_SLBRCHARGE(0x2) },
+ { 0x06, REG06_XPSAVE | REG06_NT },
+ { 0x07, 0 },
+ { 0x08, REG08_PANEL(0x1) | REG08_CLOCK_DIV(0x2) },
+ { 0x09, REG09_SUB_BRIGHT_R(0x20) },
+ { 0x0a, REG0A_SUB_BRIGHT_B(0x20) },
+ { 0x0b, REG0B_HD_FREERUN | REG0B_VD_FREERUN },
+ { 0x0c, REG0C_CONTRAST_R(0x10) },
+ { 0x0d, REG0D_CONTRAST_G(0x10) },
+ { 0x0e, REG0E_CONTRAST_B(0x10) },
+ { 0x0f, 0 },
+ { 0x10, REG10_BRIGHT(0x7f) },
+ { 0x11, REG11_SIGC_CNTL | REG11_SIG_GAIN(0x3f) },
+ { 0x12, REG12_COLOR(0x20) | REG12_PWCKSEL(0x1) },
+ { 0x13, REG13_4096LEVEL_CNTL(0x8) },
+ { 0x14, 0 },
+ { 0x15, 0 },
+};
+
+static int y030xx067a_prepare(struct drm_panel *panel)
+{
+ struct y030xx067a *priv = to_y030xx067a(panel);
+ struct device *dev = &priv->spi->dev;
+ int err;
+
+ err = regulator_enable(priv->supply);
+ if (err) {
+ dev_err(dev, "Failed to enable power supply: %d\n", err);
+ return err;
+ }
+
+ /* Reset the chip */
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ usleep_range(1000, 20000);
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ usleep_range(1000, 20000);
+
+ err = regmap_multi_reg_write(priv->map, y030xx067a_init_sequence,
+ ARRAY_SIZE(y030xx067a_init_sequence));
+ if (err) {
+ dev_err(dev, "Failed to init registers: %d\n", err);
+ goto err_disable_regulator;
+ }
+
+ msleep(120);
+
+ return 0;
+
+err_disable_regulator:
+ regulator_disable(priv->supply);
+ return err;
+}
+
+static int y030xx067a_unprepare(struct drm_panel *panel)
+{
+ struct y030xx067a *priv = to_y030xx067a(panel);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_disable(priv->supply);
+
+ return 0;
+}
+
+static int y030xx067a_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct y030xx067a *priv = to_y030xx067a(panel);
+ const struct y030xx067a_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 y030xx067a_funcs = {
+ .prepare = y030xx067a_prepare,
+ .unprepare = y030xx067a_unprepare,
+ .get_modes = y030xx067a_get_modes,
+};
+
+static const struct regmap_config y030xx067a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x15,
+};
+
+static int y030xx067a_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct y030xx067a *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->spi = spi;
+ spi_set_drvdata(spi, priv);
+
+ priv->map = devm_regmap_init_spi(spi, &y030xx067a_regmap_config);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "Unable to init regmap\n");
+ return PTR_ERR(priv->map);
+ }
+
+ 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)) {
+ dev_err(dev, "Failed to get power supply\n");
+ return PTR_ERR(priv->supply);
+ }
+
+ priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset_gpio)) {
+ dev_err(dev, "Failed to get reset GPIO\n");
+ return PTR_ERR(priv->reset_gpio);
+ }
+
+ drm_panel_init(&priv->panel, dev, &y030xx067a_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ err = drm_panel_of_backlight(&priv->panel);
+ if (err)
+ return err;
+
+ drm_panel_add(&priv->panel);
+
+ return 0;
+}
+
+static int y030xx067a_remove(struct spi_device *spi)
+{
+ struct y030xx067a *priv = spi_get_drvdata(spi);
+
+ drm_panel_remove(&priv->panel);
+ drm_panel_disable(&priv->panel);
+ drm_panel_unprepare(&priv->panel);
+
+ return 0;
+}
+
+static const struct drm_display_mode y030xx067a_modes[] = {
+ { /* 60 Hz */
+ .clock = 14400,
+ .hdisplay = 320,
+ .hsync_start = 320 + 10,
+ .hsync_end = 320 + 10 + 37,
+ .htotal = 320 + 10 + 37 + 33,
+ .vdisplay = 480,
+ .vsync_start = 480 + 84,
+ .vsync_end = 480 + 84 + 20,
+ .vtotal = 480 + 84 + 20 + 16,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+ { /* 50 Hz */
+ .clock = 12000,
+ .hdisplay = 320,
+ .hsync_start = 320 + 10,
+ .hsync_end = 320 + 10 + 37,
+ .htotal = 320 + 10 + 37 + 33,
+ .vdisplay = 480,
+ .vsync_start = 480 + 84,
+ .vsync_end = 480 + 84 + 20,
+ .vtotal = 480 + 84 + 20 + 16,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+};
+
+static const struct y030xx067a_info y030xx067a_info = {
+ .display_modes = y030xx067a_modes,
+ .num_modes = ARRAY_SIZE(y030xx067a_modes),
+ .width_mm = 69,
+ .height_mm = 51,
+ .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_LOW,
+};
+
+static const struct of_device_id y030xx067a_of_match[] = {
+ { .compatible = "abt,y030xx067a", .data = &y030xx067a_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, y030xx067a_of_match);
+
+static struct spi_driver y030xx067a_driver = {
+ .driver = {
+ .name = "abt-y030xx067a",
+ .of_match_table = y030xx067a_of_match,
+ },
+ .probe = y030xx067a_probe,
+ .remove = y030xx067a_remove,
+};
+module_spi_driver(y030xx067a_driver);
+
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
index 074e18559b9f..8e84df9a0033 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
@@ -152,7 +152,7 @@
#define ILI9322_GAMMA_7 0x16
#define ILI9322_GAMMA_8 0x17
-/**
+/*
* enum ili9322_input - the format of the incoming signal to the panel
*
* The panel can be connected to various input streams and four of them can
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
new file mode 100644
index 000000000000..533cd3934b8b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * This driver is for the DSI interface to panels using the NT36672A display driver IC
+ * from Novatek.
+ * Currently supported are the Tianma FHD+ panels found in some Xiaomi phones, including
+ * some variants of the Poco F1 phone.
+ *
+ * Panels using the Novatek NT37762A IC should add appropriate configuration per-panel and
+ * use this driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct nt36672a_panel_cmd {
+ const char data[2];
+};
+
+static const char * const nt36672a_regulator_names[] = {
+ "vddio",
+ "vddpos",
+ "vddneg",
+};
+
+static unsigned long const nt36672a_regulator_enable_loads[] = {
+ 62000,
+ 100000,
+ 100000
+};
+
+struct nt36672a_panel_desc {
+ const struct drm_display_mode *display_mode;
+ const char *panel_name;
+
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ unsigned int lanes;
+
+ unsigned int num_on_cmds_1;
+ const struct nt36672a_panel_cmd *on_cmds_1;
+ unsigned int num_on_cmds_2;
+ const struct nt36672a_panel_cmd *on_cmds_2;
+
+ unsigned int num_off_cmds;
+ const struct nt36672a_panel_cmd *off_cmds;
+};
+
+struct nt36672a_panel {
+ struct drm_panel base;
+ struct mipi_dsi_device *link;
+ const struct nt36672a_panel_desc *desc;
+
+ struct regulator_bulk_data supplies[ARRAY_SIZE(nt36672a_regulator_names)];
+
+ struct gpio_desc *reset_gpio;
+
+ bool prepared;
+};
+
+static inline struct nt36672a_panel *to_nt36672a_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct nt36672a_panel, base);
+}
+
+static int nt36672a_send_cmds(struct drm_panel *panel, const struct nt36672a_panel_cmd *cmds,
+ int num)
+{
+ struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < num; i++) {
+ const struct nt36672a_panel_cmd *cmd = &cmds[i];
+
+ err = mipi_dsi_dcs_write(pinfo->link, cmd->data[0], cmd->data + 1, 1);
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int nt36672a_panel_power_off(struct drm_panel *panel)
+{
+ struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+ int ret = 0;
+
+ gpiod_set_value(pinfo->reset_gpio, 1);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(pinfo->supplies), pinfo->supplies);
+ if (ret)
+ dev_err(panel->dev, "regulator_bulk_disable failed %d\n", ret);
+
+ return ret;
+}
+
+static int nt36672a_panel_unprepare(struct drm_panel *panel)
+{
+ struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+ int ret;
+
+ if (!pinfo->prepared)
+ return 0;
+
+ /* send off cmds */
+ ret = nt36672a_send_cmds(panel, pinfo->desc->off_cmds,
+ pinfo->desc->num_off_cmds);
+
+ if (ret < 0)
+ dev_err(panel->dev, "failed to send DCS off cmds: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_display_off(pinfo->link);
+ if (ret < 0)
+ dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
+
+ /* 120ms delay required here as per DCS spec */
+ msleep(120);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
+ if (ret < 0)
+ dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
+
+ /* 0x3C = 60ms delay */
+ msleep(60);
+
+ ret = nt36672a_panel_power_off(panel);
+ if (ret < 0)
+ dev_err(panel->dev, "power_off failed ret = %d\n", ret);
+
+ pinfo->prepared = false;
+
+ return ret;
+}
+
+static int nt36672a_panel_power_on(struct nt36672a_panel *pinfo)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pinfo->supplies), pinfo->supplies);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * As per downstream kernel, Reset sequence of Tianma FHD panel requires the panel to
+ * be out of reset for 10ms, followed by being held in reset for 10ms. But for Android
+ * AOSP, we needed to bump it upto 200ms otherwise we get white screen sometimes.
+ * FIXME: Try to reduce this 200ms to a lesser value.
+ */
+ gpiod_set_value(pinfo->reset_gpio, 1);
+ msleep(200);
+ gpiod_set_value(pinfo->reset_gpio, 0);
+ msleep(200);
+
+ return 0;
+}
+
+static int nt36672a_panel_prepare(struct drm_panel *panel)
+{
+ struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+ int err;
+
+ if (pinfo->prepared)
+ return 0;
+
+ err = nt36672a_panel_power_on(pinfo);
+ if (err < 0)
+ goto poweroff;
+
+ /* send first part of init cmds */
+ err = nt36672a_send_cmds(panel, pinfo->desc->on_cmds_1,
+ pinfo->desc->num_on_cmds_1);
+
+ if (err < 0) {
+ dev_err(panel->dev, "failed to send DCS Init 1st Code: %d\n", err);
+ goto poweroff;
+ }
+
+ err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
+ if (err < 0) {
+ dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
+ goto poweroff;
+ }
+
+ /* 0x46 = 70 ms delay */
+ msleep(70);
+
+ err = mipi_dsi_dcs_set_display_on(pinfo->link);
+ if (err < 0) {
+ dev_err(panel->dev, "failed to Set Display ON: %d\n", err);
+ goto poweroff;
+ }
+
+ /* Send rest of the init cmds */
+ err = nt36672a_send_cmds(panel, pinfo->desc->on_cmds_2,
+ pinfo->desc->num_on_cmds_2);
+
+ if (err < 0) {
+ dev_err(panel->dev, "failed to send DCS Init 2nd Code: %d\n", err);
+ goto poweroff;
+ }
+
+ msleep(120);
+
+ pinfo->prepared = true;
+
+ return 0;
+
+poweroff:
+ gpiod_set_value(pinfo->reset_gpio, 0);
+ return err;
+}
+
+static int nt36672a_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+ const struct drm_display_mode *m = pinfo->desc->display_mode;
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, m);
+ if (!mode) {
+ dev_err(panel->dev, "failed to add mode %ux%u@%u\n", m->hdisplay,
+ m->vdisplay, drm_mode_vrefresh(m));
+ return -ENOMEM;
+ }
+
+ connector->display_info.width_mm = pinfo->desc->width_mm;
+ connector->display_info.height_mm = pinfo->desc->height_mm;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs panel_funcs = {
+ .unprepare = nt36672a_panel_unprepare,
+ .prepare = nt36672a_panel_prepare,
+ .get_modes = nt36672a_panel_get_modes,
+};
+
+static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_1[] = {
+ /* skin enhancement mode */
+ { .data = {0xFF, 0x22} },
+ { .data = {0x00, 0x40} },
+ { .data = {0x01, 0xC0} },
+ { .data = {0x02, 0x40} },
+ { .data = {0x03, 0x40} },
+ { .data = {0x04, 0x40} },
+ { .data = {0x05, 0x40} },
+ { .data = {0x06, 0x40} },
+ { .data = {0x07, 0x40} },
+ { .data = {0x08, 0x40} },
+ { .data = {0x09, 0x40} },
+ { .data = {0x0A, 0x40} },
+ { .data = {0x0B, 0x40} },
+ { .data = {0x0C, 0x40} },
+ { .data = {0x0D, 0x40} },
+ { .data = {0x0E, 0x40} },
+ { .data = {0x0F, 0x40} },
+ { .data = {0x10, 0x40} },
+ { .data = {0x11, 0x50} },
+ { .data = {0x12, 0x60} },
+ { .data = {0x13, 0x70} },
+ { .data = {0x14, 0x58} },
+ { .data = {0x15, 0x68} },
+ { .data = {0x16, 0x78} },
+ { .data = {0x17, 0x77} },
+ { .data = {0x18, 0x39} },
+ { .data = {0x19, 0x2D} },
+ { .data = {0x1A, 0x2E} },
+ { .data = {0x1B, 0x32} },
+ { .data = {0x1C, 0x37} },
+ { .data = {0x1D, 0x3A} },
+ { .data = {0x1E, 0x40} },
+ { .data = {0x1F, 0x40} },
+ { .data = {0x20, 0x40} },
+ { .data = {0x21, 0x40} },
+ { .data = {0x22, 0x40} },
+ { .data = {0x23, 0x40} },
+ { .data = {0x24, 0x40} },
+ { .data = {0x25, 0x40} },
+ { .data = {0x26, 0x40} },
+ { .data = {0x27, 0x40} },
+ { .data = {0x28, 0x40} },
+ { .data = {0x2D, 0x00} },
+ { .data = {0x2F, 0x40} },
+ { .data = {0x30, 0x40} },
+ { .data = {0x31, 0x40} },
+ { .data = {0x32, 0x40} },
+ { .data = {0x33, 0x40} },
+ { .data = {0x34, 0x40} },
+ { .data = {0x35, 0x40} },
+ { .data = {0x36, 0x40} },
+ { .data = {0x37, 0x40} },
+ { .data = {0x38, 0x40} },
+ { .data = {0x39, 0x40} },
+ { .data = {0x3A, 0x40} },
+ { .data = {0x3B, 0x40} },
+ { .data = {0x3D, 0x40} },
+ { .data = {0x3F, 0x40} },
+ { .data = {0x40, 0x40} },
+ { .data = {0x41, 0x40} },
+ { .data = {0x42, 0x40} },
+ { .data = {0x43, 0x40} },
+ { .data = {0x44, 0x40} },
+ { .data = {0x45, 0x40} },
+ { .data = {0x46, 0x40} },
+ { .data = {0x47, 0x40} },
+ { .data = {0x48, 0x40} },
+ { .data = {0x49, 0x40} },
+ { .data = {0x4A, 0x40} },
+ { .data = {0x4B, 0x40} },
+ { .data = {0x4C, 0x40} },
+ { .data = {0x4D, 0x40} },
+ { .data = {0x4E, 0x40} },
+ { .data = {0x4F, 0x40} },
+ { .data = {0x50, 0x40} },
+ { .data = {0x51, 0x40} },
+ { .data = {0x52, 0x40} },
+ { .data = {0x53, 0x01} },
+ { .data = {0x54, 0x01} },
+ { .data = {0x55, 0xFE} },
+ { .data = {0x56, 0x77} },
+ { .data = {0x58, 0xCD} },
+ { .data = {0x59, 0xD0} },
+ { .data = {0x5A, 0xD0} },
+ { .data = {0x5B, 0x50} },
+ { .data = {0x5C, 0x50} },
+ { .data = {0x5D, 0x50} },
+ { .data = {0x5E, 0x50} },
+ { .data = {0x5F, 0x50} },
+ { .data = {0x60, 0x50} },
+ { .data = {0x61, 0x50} },
+ { .data = {0x62, 0x50} },
+ { .data = {0x63, 0x50} },
+ { .data = {0x64, 0x50} },
+ { .data = {0x65, 0x50} },
+ { .data = {0x66, 0x50} },
+ { .data = {0x67, 0x50} },
+ { .data = {0x68, 0x50} },
+ { .data = {0x69, 0x50} },
+ { .data = {0x6A, 0x50} },
+ { .data = {0x6B, 0x50} },
+ { .data = {0x6C, 0x50} },
+ { .data = {0x6D, 0x50} },
+ { .data = {0x6E, 0x50} },
+ { .data = {0x6F, 0x50} },
+ { .data = {0x70, 0x07} },
+ { .data = {0x71, 0x00} },
+ { .data = {0x72, 0x00} },
+ { .data = {0x73, 0x00} },
+ { .data = {0x74, 0x06} },
+ { .data = {0x75, 0x0C} },
+ { .data = {0x76, 0x03} },
+ { .data = {0x77, 0x09} },
+ { .data = {0x78, 0x0F} },
+ { .data = {0x79, 0x68} },
+ { .data = {0x7A, 0x88} },
+ { .data = {0x7C, 0x80} },
+ { .data = {0x7D, 0x80} },
+ { .data = {0x7E, 0x80} },
+ { .data = {0x7F, 0x00} },
+ { .data = {0x80, 0x00} },
+ { .data = {0x81, 0x00} },
+ { .data = {0x83, 0x01} },
+ { .data = {0x84, 0x00} },
+ { .data = {0x85, 0x80} },
+ { .data = {0x86, 0x80} },
+ { .data = {0x87, 0x80} },
+ { .data = {0x88, 0x40} },
+ { .data = {0x89, 0x91} },
+ { .data = {0x8A, 0x98} },
+ { .data = {0x8B, 0x80} },
+ { .data = {0x8C, 0x80} },
+ { .data = {0x8D, 0x80} },
+ { .data = {0x8E, 0x80} },
+ { .data = {0x8F, 0x80} },
+ { .data = {0x90, 0x80} },
+ { .data = {0x91, 0x80} },
+ { .data = {0x92, 0x80} },
+ { .data = {0x93, 0x80} },
+ { .data = {0x94, 0x80} },
+ { .data = {0x95, 0x80} },
+ { .data = {0x96, 0x80} },
+ { .data = {0x97, 0x80} },
+ { .data = {0x98, 0x80} },
+ { .data = {0x99, 0x80} },
+ { .data = {0x9A, 0x80} },
+ { .data = {0x9B, 0x80} },
+ { .data = {0x9C, 0x80} },
+ { .data = {0x9D, 0x80} },
+ { .data = {0x9E, 0x80} },
+ { .data = {0x9F, 0x80} },
+ { .data = {0xA0, 0x8A} },
+ { .data = {0xA2, 0x80} },
+ { .data = {0xA6, 0x80} },
+ { .data = {0xA7, 0x80} },
+ { .data = {0xA9, 0x80} },
+ { .data = {0xAA, 0x80} },
+ { .data = {0xAB, 0x80} },
+ { .data = {0xAC, 0x80} },
+ { .data = {0xAD, 0x80} },
+ { .data = {0xAE, 0x80} },
+ { .data = {0xAF, 0x80} },
+ { .data = {0xB7, 0x76} },
+ { .data = {0xB8, 0x76} },
+ { .data = {0xB9, 0x05} },
+ { .data = {0xBA, 0x0D} },
+ { .data = {0xBB, 0x14} },
+ { .data = {0xBC, 0x0F} },
+ { .data = {0xBD, 0x18} },
+ { .data = {0xBE, 0x1F} },
+ { .data = {0xBF, 0x05} },
+ { .data = {0xC0, 0x0D} },
+ { .data = {0xC1, 0x14} },
+ { .data = {0xC2, 0x03} },
+ { .data = {0xC3, 0x07} },
+ { .data = {0xC4, 0x0A} },
+ { .data = {0xC5, 0xA0} },
+ { .data = {0xC6, 0x55} },
+ { .data = {0xC7, 0xFF} },
+ { .data = {0xC8, 0x39} },
+ { .data = {0xC9, 0x44} },
+ { .data = {0xCA, 0x12} },
+ { .data = {0xCD, 0x80} },
+ { .data = {0xDB, 0x80} },
+ { .data = {0xDC, 0x80} },
+ { .data = {0xDD, 0x80} },
+ { .data = {0xE0, 0x80} },
+ { .data = {0xE1, 0x80} },
+ { .data = {0xE2, 0x80} },
+ { .data = {0xE3, 0x80} },
+ { .data = {0xE4, 0x80} },
+ { .data = {0xE5, 0x40} },
+ { .data = {0xE6, 0x40} },
+ { .data = {0xE7, 0x40} },
+ { .data = {0xE8, 0x40} },
+ { .data = {0xE9, 0x40} },
+ { .data = {0xEA, 0x40} },
+ { .data = {0xEB, 0x40} },
+ { .data = {0xEC, 0x40} },
+ { .data = {0xED, 0x40} },
+ { .data = {0xEE, 0x40} },
+ { .data = {0xEF, 0x40} },
+ { .data = {0xF0, 0x40} },
+ { .data = {0xF1, 0x40} },
+ { .data = {0xF2, 0x40} },
+ { .data = {0xF3, 0x40} },
+ { .data = {0xF4, 0x40} },
+ { .data = {0xF5, 0x40} },
+ { .data = {0xF6, 0x40} },
+ { .data = {0xFB, 0x1} },
+ { .data = {0xFF, 0x23} },
+ { .data = {0xFB, 0x01} },
+ /* dimming enable */
+ { .data = {0x01, 0x84} },
+ { .data = {0x05, 0x2D} },
+ { .data = {0x06, 0x00} },
+ /* resolution 1080*2246 */
+ { .data = {0x11, 0x01} },
+ { .data = {0x12, 0x7B} },
+ { .data = {0x15, 0x6F} },
+ { .data = {0x16, 0x0B} },
+ /* UI mode */
+ { .data = {0x29, 0x0A} },
+ { .data = {0x30, 0xFF} },
+ { .data = {0x31, 0xFF} },
+ { .data = {0x32, 0xFF} },
+ { .data = {0x33, 0xFF} },
+ { .data = {0x34, 0xFF} },
+ { .data = {0x35, 0xFF} },
+ { .data = {0x36, 0xFF} },
+ { .data = {0x37, 0xFF} },
+ { .data = {0x38, 0xFC} },
+ { .data = {0x39, 0xF8} },
+ { .data = {0x3A, 0xF4} },
+ { .data = {0x3B, 0xF1} },
+ { .data = {0x3D, 0xEE} },
+ { .data = {0x3F, 0xEB} },
+ { .data = {0x40, 0xE8} },
+ { .data = {0x41, 0xE5} },
+ /* STILL mode */
+ { .data = {0x2A, 0x13} },
+ { .data = {0x45, 0xFF} },
+ { .data = {0x46, 0xFF} },
+ { .data = {0x47, 0xFF} },
+ { .data = {0x48, 0xFF} },
+ { .data = {0x49, 0xFF} },
+ { .data = {0x4A, 0xFF} },
+ { .data = {0x4B, 0xFF} },
+ { .data = {0x4C, 0xFF} },
+ { .data = {0x4D, 0xED} },
+ { .data = {0x4E, 0xD5} },
+ { .data = {0x4F, 0xBF} },
+ { .data = {0x50, 0xA6} },
+ { .data = {0x51, 0x96} },
+ { .data = {0x52, 0x86} },
+ { .data = {0x53, 0x76} },
+ { .data = {0x54, 0x66} },
+ /* MOVING mode */
+ { .data = {0x2B, 0x0E} },
+ { .data = {0x58, 0xFF} },
+ { .data = {0x59, 0xFF} },
+ { .data = {0x5A, 0xFF} },
+ { .data = {0x5B, 0xFF} },
+ { .data = {0x5C, 0xFF} },
+ { .data = {0x5D, 0xFF} },
+ { .data = {0x5E, 0xFF} },
+ { .data = {0x5F, 0xFF} },
+ { .data = {0x60, 0xF6} },
+ { .data = {0x61, 0xEA} },
+ { .data = {0x62, 0xE1} },
+ { .data = {0x63, 0xD8} },
+ { .data = {0x64, 0xCE} },
+ { .data = {0x65, 0xC3} },
+ { .data = {0x66, 0xBA} },
+ { .data = {0x67, 0xB3} },
+ { .data = {0xFF, 0x25} },
+ { .data = {0xFB, 0x01} },
+ { .data = {0x05, 0x04} },
+ { .data = {0xFF, 0x26} },
+ { .data = {0xFB, 0x01} },
+ { .data = {0x1C, 0xAF} },
+ { .data = {0xFF, 0x10} },
+ { .data = {0xFB, 0x01} },
+ { .data = {0x51, 0xFF} },
+ { .data = {0x53, 0x24} },
+ { .data = {0x55, 0x00} },
+};
+
+static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_2[] = {
+ { .data = {0xFF, 0x24} },
+ { .data = {0xFB, 0x01} },
+ { .data = {0xC3, 0x01} },
+ { .data = {0xC4, 0x54} },
+ { .data = {0xFF, 0x10} },
+};
+
+static const struct nt36672a_panel_cmd tianma_fhd_video_off_cmds[] = {
+ { .data = {0xFF, 0x24} },
+ { .data = {0xFB, 0x01} },
+ { .data = {0xC3, 0x01} },
+ { .data = {0xFF, 0x10} },
+};
+
+static const struct drm_display_mode tianma_fhd_video_panel_default_mode = {
+ .clock = 161331,
+
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 40,
+ .hsync_end = 1080 + 40 + 20,
+ .htotal = 1080 + 40 + 20 + 44,
+
+ .vdisplay = 2246,
+ .vsync_start = 2246 + 15,
+ .vsync_end = 2246 + 15 + 2,
+ .vtotal = 2246 + 15 + 2 + 8,
+
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct nt36672a_panel_desc tianma_fhd_video_panel_desc = {
+ .display_mode = &tianma_fhd_video_panel_default_mode,
+
+ .width_mm = 68,
+ .height_mm = 136,
+
+ .mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO
+ | MIPI_DSI_MODE_VIDEO_HSE
+ | MIPI_DSI_CLOCK_NON_CONTINUOUS
+ | MIPI_DSI_MODE_VIDEO_BURST,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .on_cmds_1 = tianma_fhd_video_on_cmds_1,
+ .num_on_cmds_1 = ARRAY_SIZE(tianma_fhd_video_on_cmds_1),
+ .on_cmds_2 = tianma_fhd_video_on_cmds_2,
+ .num_on_cmds_2 = ARRAY_SIZE(tianma_fhd_video_on_cmds_2),
+ .off_cmds = tianma_fhd_video_off_cmds,
+ .num_off_cmds = ARRAY_SIZE(tianma_fhd_video_off_cmds),
+};
+
+static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
+{
+ struct device *dev = &pinfo->link->dev;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(pinfo->supplies); i++)
+ pinfo->supplies[i].supply = nt36672a_regulator_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pinfo->supplies),
+ pinfo->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ for (i = 0; i < ARRAY_SIZE(pinfo->supplies); i++) {
+ ret = regulator_set_load(pinfo->supplies[i].consumer,
+ nt36672a_regulator_enable_loads[i]);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set regulator enable loads\n");
+ }
+
+ pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pinfo->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio),
+ "failed to get reset gpio from DT\n");
+
+ drm_panel_init(&pinfo->base, dev, &panel_funcs, DRM_MODE_CONNECTOR_DSI);
+
+ drm_panel_add(&pinfo->base);
+
+ return 0;
+}
+
+static int nt36672a_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct nt36672a_panel *pinfo;
+ const struct nt36672a_panel_desc *desc;
+ int err;
+
+ pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(&dsi->dev);
+ dsi->mode_flags = desc->mode_flags;
+ dsi->format = desc->format;
+ dsi->lanes = desc->lanes;
+ pinfo->desc = desc;
+ pinfo->link = dsi;
+
+ mipi_dsi_set_drvdata(dsi, pinfo);
+
+ err = nt36672a_panel_add(pinfo);
+ if (err < 0)
+ return err;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static int nt36672a_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct nt36672a_panel *pinfo = mipi_dsi_get_drvdata(dsi);
+ int err;
+
+ err = drm_panel_unprepare(&pinfo->base);
+ if (err < 0)
+ dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
+
+ err = drm_panel_disable(&pinfo->base);
+ if (err < 0)
+ dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
+
+ err = mipi_dsi_detach(dsi);
+ if (err < 0)
+ dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
+
+ drm_panel_remove(&pinfo->base);
+
+ return 0;
+}
+
+static void nt36672a_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct nt36672a_panel *pinfo = mipi_dsi_get_drvdata(dsi);
+
+ drm_panel_disable(&pinfo->base);
+ drm_panel_unprepare(&pinfo->base);
+}
+
+static const struct of_device_id tianma_fhd_video_of_match[] = {
+ { .compatible = "tianma,fhd-video", .data = &tianma_fhd_video_panel_desc },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tianma_fhd_video_of_match);
+
+static struct mipi_dsi_driver nt36672a_panel_driver = {
+ .driver = {
+ .name = "panel-tianma-nt36672a",
+ .of_match_table = tianma_fhd_video_of_match,
+ },
+ .probe = nt36672a_panel_probe,
+ .remove = nt36672a_panel_remove,
+ .shutdown = nt36672a_panel_shutdown,
+};
+module_mipi_dsi_driver(nt36672a_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_DESCRIPTION("NOVATEK NT36672A based MIPI-DSI LCD panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
index b6e377aa1131..f80b44a8a700 100644
--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
+++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
@@ -99,20 +99,6 @@ static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n");
}
-static void otm8009a_dcs_write_buf_hs(struct otm8009a *ctx, const void *data,
- size_t len)
-{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
-
- /* data will be sent in dsi hs mode (ie. no lpm) */
- dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
-
- otm8009a_dcs_write_buf(ctx, data, len);
-
- /* restore back the dsi lpm mode */
- dsi->mode_flags |= MIPI_DSI_MODE_LPM;
-}
-
#define dcs_write_seq(ctx, seq...) \
({ \
static const u8 d[] = { seq }; \
@@ -400,7 +386,7 @@ static int otm8009a_backlight_update_status(struct backlight_device *bd)
*/
data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
data[1] = bd->props.brightness;
- otm8009a_dcs_write_buf_hs(ctx, data, ARRAY_SIZE(data));
+ otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
/* set Brightness Control & Backlight on */
data[1] = 0x24;
@@ -412,7 +398,7 @@ static int otm8009a_backlight_update_status(struct backlight_device *bd)
/* Update Brightness Control & Backlight */
data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
- otm8009a_dcs_write_buf_hs(ctx, data, ARRAY_SIZE(data));
+ otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
return 0;
}
@@ -452,7 +438,7 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_LPM;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs,
DRM_MODE_CONNECTOR_DSI);
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
index f908eeafb1af..412c0dbcb2b6 100644
--- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c
+++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
@@ -82,15 +82,15 @@ struct rm68200 {
};
static const struct drm_display_mode default_mode = {
- .clock = 52582,
+ .clock = 54000,
.hdisplay = 720,
- .hsync_start = 720 + 38,
- .hsync_end = 720 + 38 + 8,
- .htotal = 720 + 38 + 8 + 38,
+ .hsync_start = 720 + 48,
+ .hsync_end = 720 + 48 + 9,
+ .htotal = 720 + 48 + 9 + 48,
.vdisplay = 1280,
.vsync_start = 1280 + 12,
- .vsync_end = 1280 + 12 + 4,
- .vtotal = 1280 + 12 + 4 + 12,
+ .vsync_end = 1280 + 12 + 5,
+ .vtotal = 1280 + 12 + 5 + 12,
.flags = 0,
.width_mm = 68,
.height_mm = 122,
@@ -391,7 +391,7 @@ static int rm68200_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_LPM;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, dev, &rm68200_drm_funcs,
DRM_MODE_CONNECTOR_DSI);
diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
index 535c8d1cca21..a3782830ae3c 100644
--- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
+++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
@@ -75,13 +75,8 @@ static int rb070d30_panel_unprepare(struct drm_panel *panel)
static int rb070d30_panel_enable(struct drm_panel *panel)
{
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
- int ret;
- ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
- if (ret)
- return ret;
-
- return 0;
+ return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
}
static int rb070d30_panel_disable(struct drm_panel *panel)
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
index 1d1c79a18613..0ab1b7ec84cd 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
@@ -214,7 +214,7 @@ static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
0x00, 0x00 }
};
-unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
+static const unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
0x18, 0x19, 0x1a, 0x1b, 0x1c,
0x1d, 0x1e, 0x1f, 0x20, 0x21
};
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
index d298d780220d..326deb3177b6 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
@@ -13,28 +13,28 @@
static int s6e63m0_spi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
{
- /*
- * FIXME: implement reading DCS commands over SPI so we can
- * properly identify which physical panel is connected.
- */
- *data = 0;
+ struct spi_device *spi = to_spi_device(dev);
+ u16 buf[1];
+ u16 rbuf[1];
+ int ret;
+
+ /* SPI buffers are always in CPU order */
+ buf[0] = (u16)cmd;
+ ret = spi_write_then_read(spi, buf, 2, rbuf, 2);
+ dev_dbg(dev, "READ CMD: %04x RET: %04x\n", buf[0], rbuf[0]);
+ if (!ret)
+ /* These high 8 bits of the 9 contains the readout */
+ *data = (rbuf[0] & 0x1ff) >> 1;
- return 0;
+ return ret;
}
static int s6e63m0_spi_write_word(struct device *dev, u16 data)
{
struct spi_device *spi = to_spi_device(dev);
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = &data,
- };
- struct spi_message msg;
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
- return spi_sync(spi, &msg);
+ /* SPI buffers are always in CPU order */
+ return spi_write(spi, &data, 2);
}
static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len)
@@ -42,10 +42,17 @@ static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len)
int ret = 0;
dev_dbg(dev, "SPI writing dcs seq: %*ph\n", (int)len, data);
+
+ /*
+ * This sends 9 bits with the first bit (bit 8) set to 0
+ * This indicates that this is a command. Anything after the
+ * command is data.
+ */
ret = s6e63m0_spi_write_word(dev, *data);
while (!ret && --len) {
++data;
+ /* This sends 9 bits with the first bit (bit 8) set to 1 */
ret = s6e63m0_spi_write_word(dev, *data | DATA_MASK);
}
@@ -65,7 +72,8 @@ static int s6e63m0_spi_probe(struct spi_device *spi)
int ret;
spi->bits_per_word = 9;
- spi->mode = SPI_MODE_3;
+ /* Preserve e.g. SPI_3WIRE setting */
+ spi->mode |= SPI_MODE_3;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(dev, "spi setup failed.\n");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
index 3eee67e2d86a..210e70da3a15 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
@@ -16,6 +16,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
+#include <linux/media-bus-format.h>
#include <video/mipi_display.h>
@@ -410,6 +411,7 @@ static int s6e63m0_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct drm_display_mode *mode;
+ static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
@@ -419,6 +421,13 @@ static int s6e63m0_get_modes(struct drm_panel *panel,
return -ENOMEM;
}
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
new file mode 100644
index 000000000000..8cb1853574bb
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020 Caleb Connolly <caleb@connolly.tech>
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/swab.h>
+#include <linux/backlight.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct sofef00_panel {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+ const struct drm_display_mode *mode;
+ bool prepared;
+};
+
+static inline
+struct sofef00_panel *to_sofef00_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct sofef00_panel, panel);
+}
+
+#define dsi_dcs_write_seq(dsi, seq...) do { \
+ static const u8 d[] = { seq }; \
+ int ret; \
+ ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+static void sofef00_panel_reset(struct sofef00_panel *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(2000, 3000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(12000, 13000);
+}
+
+static int sofef00_panel_on(struct sofef00_panel *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ usleep_range(10000, 11000);
+
+ dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear on: %d\n", ret);
+ return ret;
+ }
+
+ dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ dsi_dcs_write_seq(dsi, 0xb0, 0x07);
+ dsi_dcs_write_seq(dsi, 0xb6, 0x12);
+ dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set display on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sofef00_panel_off(struct sofef00_panel *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set display off: %d\n", ret);
+ return ret;
+ }
+ msleep(40);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(160);
+
+ return 0;
+}
+
+static int sofef00_panel_prepare(struct drm_panel *panel)
+{
+ struct sofef00_panel *ctx = to_sofef00_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->supply);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+
+ sofef00_panel_reset(ctx);
+
+ ret = sofef00_panel_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ return ret;
+ }
+
+ ctx->prepared = true;
+ return 0;
+}
+
+static int sofef00_panel_unprepare(struct drm_panel *panel)
+{
+ struct sofef00_panel *ctx = to_sofef00_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = sofef00_panel_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ regulator_disable(ctx->supply);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static const struct drm_display_mode enchilada_panel_mode = {
+ .clock = (1080 + 112 + 16 + 36) * (2280 + 36 + 8 + 12) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 112,
+ .hsync_end = 1080 + 112 + 16,
+ .htotal = 1080 + 112 + 16 + 36,
+ .vdisplay = 2280,
+ .vsync_start = 2280 + 36,
+ .vsync_end = 2280 + 36 + 8,
+ .vtotal = 2280 + 36 + 8 + 12,
+ .width_mm = 68,
+ .height_mm = 145,
+};
+
+static const struct drm_display_mode fajita_panel_mode = {
+ .clock = (1080 + 72 + 16 + 36) * (2340 + 32 + 4 + 18) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 72,
+ .hsync_end = 1080 + 72 + 16,
+ .htotal = 1080 + 72 + 16 + 36,
+ .vdisplay = 2340,
+ .vsync_start = 2340 + 32,
+ .vsync_end = 2340 + 32 + 4,
+ .vtotal = 2340 + 32 + 4 + 18,
+ .width_mm = 68,
+ .height_mm = 145,
+};
+
+static int sofef00_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ struct sofef00_panel *ctx = to_sofef00_panel(panel);
+
+ mode = drm_mode_duplicate(connector->dev, ctx->mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs sofef00_panel_panel_funcs = {
+ .prepare = sofef00_panel_prepare,
+ .unprepare = sofef00_panel_unprepare,
+ .get_modes = sofef00_panel_get_modes,
+};
+
+static int sofef00_panel_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ int err;
+ u16 brightness;
+
+ brightness = (u16)backlight_get_brightness(bl);
+ // This panel needs the high and low bytes swapped for the brightness value
+ brightness = __swab16(brightness);
+
+ err = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static const struct backlight_ops sofef00_panel_bl_ops = {
+ .update_status = sofef00_panel_bl_update_status,
+};
+
+static struct backlight_device *
+sofef00_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 1023,
+ .max_brightness = 1023,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &sofef00_panel_bl_ops, &props);
+}
+
+static int sofef00_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct sofef00_panel *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->mode = of_device_get_match_data(dev);
+
+ if (!ctx->mode) {
+ dev_err(dev, "Missing device mode\n");
+ return -ENODEV;
+ }
+
+ ctx->supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(ctx->supply)) {
+ ret = PTR_ERR(ctx->supply);
+ dev_err(dev, "Failed to get vddio regulator: %d\n", ret);
+ return ret;
+ }
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio)) {
+ ret = PTR_ERR(ctx->reset_gpio);
+ dev_warn(dev, "Failed to get reset-gpios: %d\n", ret);
+ return ret;
+ }
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+
+ drm_panel_init(&ctx->panel, dev, &sofef00_panel_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ctx->panel.backlight = sofef00_create_backlight(dsi);
+ if (IS_ERR(ctx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+ "Failed to create backlight\n");
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sofef00_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+
+ return 0;
+}
+
+static const struct of_device_id sofef00_panel_of_match[] = {
+ { // OnePlus 6 / enchilada
+ .compatible = "samsung,sofef00",
+ .data = &enchilada_panel_mode,
+ },
+ { // OnePlus 6T / fajita
+ .compatible = "samsung,s6e3fc2x01",
+ .data = &fajita_panel_mode,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sofef00_panel_of_match);
+
+static struct mipi_dsi_driver sofef00_panel_driver = {
+ .probe = sofef00_panel_probe,
+ .remove = sofef00_panel_remove,
+ .driver = {
+ .name = "panel-oneplus6",
+ .of_match_table = sofef00_panel_of_match,
+ },
+};
+
+module_mipi_dsi_driver(sofef00_panel_driver);
+
+MODULE_AUTHOR("Caleb Connolly <caleb@connolly.tech>");
+MODULE_DESCRIPTION("DRM driver for Samsung AMOLED DSI panels found in OnePlus 6/6T phones");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 2be358fb46f7..597f676a6591 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -39,6 +39,7 @@
#include <drm/drm_panel.h>
/**
+ * struct panel_desc
* @modes: Pointer to array of fixed modes appropriate for this panel. If
* only one mode then this can just be the address of this the mode.
* NOTE: cannot be used with "timings" and also if this is specified
@@ -53,6 +54,7 @@
* @delay: Structure containing various delay values for this panel.
* @bus_format: See MEDIA_BUS_FMT_... defines.
* @bus_flags: See DRM_BUS_FLAG_... defines.
+ * @connector_type: LVDS, eDP, DSI, DPI, etc.
*/
struct panel_desc {
const struct drm_display_mode *modes;
@@ -1327,6 +1329,7 @@ static const struct drm_display_mode boe_nv133fhm_n61_modes = {
.vsync_start = 1080 + 3,
.vsync_end = 1080 + 3 + 6,
.vtotal = 1080 + 3 + 6 + 31,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC,
};
/* Also used for boe_nv133fhm_n62 */
@@ -1812,6 +1815,7 @@ static const struct panel_desc edt_etm0700g0dh6 = {
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct panel_desc edt_etm0700g0bdh6 = {
@@ -3873,6 +3877,32 @@ static const struct panel_desc winstar_wf35ltiacd = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
+static const struct drm_display_mode yes_optoelectronics_ytc700tlag_05_201c_mode = {
+ .clock = 51200,
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 100,
+ .hsync_end = 1024 + 100 + 100,
+ .htotal = 1024 + 100 + 100 + 120,
+ .vdisplay = 600,
+ .vsync_start = 600 + 10,
+ .vsync_end = 600 + 10 + 10,
+ .vtotal = 600 + 10 + 10 + 15,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static const struct panel_desc yes_optoelectronics_ytc700tlag_05_201c = {
+ .modes = &yes_optoelectronics_ytc700tlag_05_201c_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 154,
+ .height = 90,
+ },
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode arm_rtsm_mode[] = {
{
.clock = 65000,
@@ -4300,6 +4330,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "winstar,wf35ltiacd",
.data = &winstar_wf35ltiacd,
}, {
+ .compatible = "yes-optoelectronics,ytc700tlag-05-201c",
+ .data = &yes_optoelectronics_ytc700tlag_05_201c,
+ }, {
/* Must be the last entry */
.compatible = "panel-dpi",
.data = &panel_dpi,
@@ -4644,8 +4677,10 @@ static int __init panel_simple_init(void)
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
- if (err < 0)
+ if (err < 0) {
+ platform_driver_unregister(&panel_simple_platform_driver);
return err;
+ }
}
return 0;
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
index c22e7c49e077..b30510b1696a 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
@@ -153,7 +153,7 @@ static const struct drm_display_mode jh057n00900_mode = {
.height_mm = 130,
};
-struct st7703_panel_desc jh057n00900_panel_desc = {
+static const struct st7703_panel_desc jh057n00900_panel_desc = {
.mode = &jh057n00900_mode,
.lanes = 4,
.mode_flags = MIPI_DSI_MODE_VIDEO |
diff --git a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
new file mode 100644
index 000000000000..820731be7147
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct tdo_tl070wsh30_panel {
+ struct drm_panel base;
+ struct mipi_dsi_device *link;
+
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+
+ bool prepared;
+};
+
+static inline
+struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct tdo_tl070wsh30_panel, base);
+}
+
+static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
+{
+ struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
+ int err;
+
+ if (tdo_tl070wsh30->prepared)
+ return 0;
+
+ err = regulator_enable(tdo_tl070wsh30->supply);
+ if (err < 0)
+ return err;
+
+ usleep_range(10000, 11000);
+
+ gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1);
+
+ usleep_range(10000, 11000);
+
+ gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0);
+
+ msleep(200);
+
+ err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
+ if (err < 0) {
+ dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
+ regulator_disable(tdo_tl070wsh30->supply);
+ return err;
+ }
+
+ msleep(200);
+
+ err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
+ if (err < 0) {
+ dev_err(panel->dev, "failed to set display on: %d\n", err);
+ regulator_disable(tdo_tl070wsh30->supply);
+ return err;
+ }
+
+ msleep(20);
+
+ tdo_tl070wsh30->prepared = true;
+
+ return 0;
+}
+
+static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
+{
+ struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
+ int err;
+
+ if (!tdo_tl070wsh30->prepared)
+ return 0;
+
+ err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
+ if (err < 0)
+ dev_err(panel->dev, "failed to set display off: %d\n", err);
+
+ usleep_range(10000, 11000);
+
+ err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
+ if (err < 0) {
+ dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
+ return err;
+ }
+
+ usleep_range(10000, 11000);
+
+ regulator_disable(tdo_tl070wsh30->supply);
+
+ tdo_tl070wsh30->prepared = false;
+
+ return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+ .clock = 47250,
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 46,
+ .hsync_end = 1024 + 46 + 80,
+ .htotal = 1024 + 46 + 80 + 100,
+ .vdisplay = 600,
+ .vsync_start = 600 + 5,
+ .vsync_end = 600 + 5 + 5,
+ .vtotal = 600 + 5 + 5 + 20,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
+ if (!mode) {
+ dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+ default_mode.hdisplay, default_mode.vdisplay,
+ drm_mode_vrefresh(&default_mode));
+ return -ENOMEM;
+ }
+
+ drm_mode_set_name(mode);
+
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = 154;
+ connector->display_info.height_mm = 85;
+ connector->display_info.bpc = 8;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = {
+ .unprepare = tdo_tl070wsh30_panel_unprepare,
+ .prepare = tdo_tl070wsh30_panel_prepare,
+ .get_modes = tdo_tl070wsh30_panel_get_modes,
+};
+
+static const struct of_device_id tdo_tl070wsh30_of_match[] = {
+ { .compatible = "tdo,tl070wsh30", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match);
+
+static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
+{
+ struct device *dev = &tdo_tl070wsh30->link->dev;
+ int err;
+
+ tdo_tl070wsh30->supply = devm_regulator_get(dev, "power");
+ if (IS_ERR(tdo_tl070wsh30->supply))
+ return PTR_ERR(tdo_tl070wsh30->supply);
+
+ tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(tdo_tl070wsh30->reset_gpio)) {
+ err = PTR_ERR(tdo_tl070wsh30->reset_gpio);
+ dev_dbg(dev, "failed to get reset gpio: %d\n", err);
+ return err;
+ }
+
+ drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
+ &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
+
+ err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
+ if (err)
+ return err;
+
+ drm_panel_add(&tdo_tl070wsh30->base);
+
+ return 0;
+}
+
+static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct tdo_tl070wsh30_panel *tdo_tl070wsh30;
+ int err;
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
+
+ tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
+ GFP_KERNEL);
+ if (!tdo_tl070wsh30)
+ return -ENOMEM;
+
+ mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
+ tdo_tl070wsh30->link = dsi;
+
+ err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30);
+ if (err < 0)
+ return err;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static int tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
+ int err;
+
+ err = mipi_dsi_detach(dsi);
+ if (err < 0)
+ dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
+
+ drm_panel_remove(&tdo_tl070wsh30->base);
+ drm_panel_disable(&tdo_tl070wsh30->base);
+ drm_panel_unprepare(&tdo_tl070wsh30->base);
+
+ return 0;
+}
+
+static void tdo_tl070wsh30_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
+
+ drm_panel_disable(&tdo_tl070wsh30->base);
+ drm_panel_unprepare(&tdo_tl070wsh30->base);
+}
+
+static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = {
+ .driver = {
+ .name = "panel-tdo-tl070wsh30",
+ .of_match_table = tdo_tl070wsh30_of_match,
+ },
+ .probe = tdo_tl070wsh30_panel_probe,
+ .remove = tdo_tl070wsh30_panel_remove,
+ .shutdown = tdo_tl070wsh30_panel_shutdown,
+};
+module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("TDO TL070WSH30 panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
index 037c14fd6bac..ba0c00d1a001 100644
--- a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
+++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
@@ -242,13 +242,8 @@ static int td028ttec1_prepare(struct drm_panel *panel)
static int td028ttec1_enable(struct drm_panel *panel)
{
struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
- int ret;
- ret = jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, NULL);
- if (ret)
- return ret;
-
- return 0;
+ return jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, NULL);
}
static int td028ttec1_disable(struct drm_panel *panel)
diff --git a/drivers/gpu/drm/panel/panel-tpo-tpg110.c b/drivers/gpu/drm/panel/panel-tpo-tpg110.c
index d57ed75a977c..e3791dad6830 100644
--- a/drivers/gpu/drm/panel/panel-tpo-tpg110.c
+++ b/drivers/gpu/drm/panel/panel-tpo-tpg110.c
@@ -76,7 +76,7 @@ struct tpg110 {
*/
struct drm_panel panel;
/**
- * @panel_type: the panel mode as detected
+ * @panel_mode: the panel mode as detected
*/
const struct tpg110_panel_mode *panel_mode;
/**
@@ -362,6 +362,7 @@ static int tpg110_enable(struct drm_panel *panel)
/**
* tpg110_get_modes() - return the appropriate mode
* @panel: the panel to get the mode for
+ * @connector: reference to the central DRM connector control structure
*
* This currently does not present a forest of modes, instead it
* presents the mode that is configured for the system under use,