aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/panel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-09-01 11:26:46 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-09-01 11:26:46 -0700
commit477f70cd2a67904e04c2c2b9bd0fa2e95222f2f6 (patch)
tree1897dd1de49e1ea24897163533e2d8ead5dad0ad /drivers/gpu/drm/panel
parentMerge tag 'media/v5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media (diff)
parentMerge tag 'amd-drm-next-5.15-2021-08-27' of https://gitlab.freedesktop.org/agd5f/linux into drm-next (diff)
downloadlinux-dev-477f70cd2a67904e04c2c2b9bd0fa2e95222f2f6.tar.xz
linux-dev-477f70cd2a67904e04c2c2b9bd0fa2e95222f2f6.zip
Merge tag 'drm-next-2021-08-31-1' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "Highlights: - i915 has seen a lot of refactoring and uAPI cleanups due to a change in the upstream direction going forward This has all been audited with known userspace, but there may be some pitfalls that were missed. - i915 now uses common TTM to enable discrete memory on DG1/2 GPUs - i915 enables Jasper and Elkhart Lake by default and has preliminary XeHP/DG2 support - amdgpu adds support for Cyan Skillfish - lots of implicit fencing rules documented and fixed up in drivers - msm now uses the core scheduler - the irq midlayer has been removed for non-legacy drivers - the sysfb code now works on more than x86. Otherwise the usual smattering of stuff everywhere, panels, bridges, refactorings. Detailed summary: core: - extract i915 eDP backlight into core - DP aux bus support - drm_device.irq_enabled removed - port drivers to native irq interfaces - export gem shadow plane handling for vgem - print proper driver name in framebuffer registration - driver fixes for implicit fencing rules - ARM fixed rate compression modifier added - updated fb damage handling - rmfb ioctl logging/docs - drop drm_gem_object_put_locked - define DRM_FORMAT_MAX_PLANES - add gem fb vmap/vunmap helpers - add lockdep_assert(once) helpers - mark drm irq midlayer as legacy - use offset adjusted bo mapping conversion vgaarb: - cleanups fbdev: - extend efifb handling to all arches - div by 0 fixes for multiple drivers udmabuf: - add hugepage mapping support dma-buf: - non-dynamic exporter fixups - document implicit fencing rules amdgpu: - Initial Cyan Skillfish support - switch virtual DCE over to vkms based atomic - VCN/JPEG power down fixes - NAVI PCIE link handling fixes - AMD HDMI freesync fixes - Yellow Carp + Beige Goby fixes - Clockgating/S0ix/SMU/EEPROM fixes - embed hw fence in job - rework dma-resv handling - ensure eviction to system ram amdkfd: - uapi: SVM address range query added - sysfs leak fix - GPUVM TLB optimizations - vmfault/migration counters i915: - Enable JSL and EHL by default - preliminary XeHP/DG2 support - remove all CNL support (never shipped) - move to TTM for discrete memory support - allow mixed object mmap handling - GEM uAPI spring cleaning - add I915_MMAP_OBJECT_FIXED - reinstate ADL-P mmap ioctls - drop a bunch of unused by userspace features - disable and remove GPU relocations - revert some i915 misfeatures - major refactoring of GuC for Gen11+ - execbuffer object locking separate step - reject caching/set-domain on discrete - Enable pipe DMC loading on XE-LPD and ADL-P - add PSF GV point support - Refactor and fix DDI buffer translations - Clean up FBC CFB allocation code - Finish INTEL_GEN() and friends macro conversions nouveau: - add eDP backlight support - implicit fence fix msm: - a680/7c3 support - drm/scheduler conversion panfrost: - rework GPU reset virtio: - fix fencing for planes ast: - add detect support bochs: - move to tiny GPU driver vc4: - use hotplug irqs - HDMI codec support vmwgfx: - use internal vmware device headers ingenic: - demidlayering irq rcar-du: - shutdown fixes - convert to bridge connector helpers zynqmp-dsub: - misc fixes mgag200: - convert PLL handling to atomic mediatek: - MT8133 AAL support - gem mmap object support - MT8167 support etnaviv: - NXP Layerscape LS1028A SoC support - GEM mmap cleanups tegra: - new user API exynos: - missing unlock fix - build warning fix - use refcount_t" * tag 'drm-next-2021-08-31-1' of git://anongit.freedesktop.org/drm/drm: (1318 commits) drm/amd/display: Move AllowDRAMSelfRefreshOrDRAMClockChangeInVblank to bounding box drm/amd/display: Remove duplicate dml init drm/amd/display: Update bounding box states (v2) drm/amd/display: Update number of DCN3 clock states drm/amdgpu: disable GFX CGCG in aldebaran drm/amdgpu: Clear RAS interrupt status on aldebaran drm/amdgpu: Add support for RAS XGMI err query drm/amdkfd: Account for SH/SE count when setting up cu masks. drm/amdgpu: rename amdgpu_bo_get_preferred_pin_domain drm/amdgpu: drop redundant cancel_delayed_work_sync call drm/amdgpu: add missing cleanups for more ASICs on UVD/VCE suspend drm/amdgpu: add missing cleanups for Polaris12 UVD/VCE on suspend drm/amdkfd: map SVM range with correct access permission drm/amdkfd: check access permisson to restore retry fault drm/amdgpu: Update RAS XGMI Error Query drm/amdgpu: Add driver infrastructure for MCA RAS drm/amd/display: Add Logging for HDMI color depth information drm/amd/amdgpu: consolidate PSP TA init shared buf functions drm/amd/amdgpu: add name field back to ras_common_if drm/amdgpu: Fix build with missing pm_suspend_target_state module export ...
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r--drivers/gpu/drm/panel/Kconfig54
-rw-r--r--drivers/gpu/drm/panel/Makefile5
-rw-r--r--drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c2
-rw-r--r--drivers/gpu/drm/panel/panel-dsi-cm.c2
-rw-r--r--drivers/gpu/drm/panel/panel-elida-kd35t133.c2
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9341.c792
-rw-r--r--drivers/gpu/drm/panel/panel-innolux-ej030na.c289
-rw-r--r--drivers/gpu/drm/panel/panel-khadas-ts050.c2
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c2
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c2
-rw-r--r--drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-atna33xc20.c366
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-db7430.c347
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c10
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c83
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c41
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.h33
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c4
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c2
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c357
-rw-r--r--drivers/gpu/drm/panel/panel-sony-acx424akp.c82
-rw-r--r--drivers/gpu/drm/panel/panel-widechips-ws2401.c441
-rw-r--r--drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c2
24 files changed, 2751 insertions, 173 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index ef87d92cdf49..beb581b96ecd 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -82,6 +82,7 @@ config DRM_PANEL_SIMPLE
depends on BACKLIGHT_CLASS_DEVICE
depends on PM
select VIDEOMODE_HELPERS
+ select DRM_DP_AUX_BUS
help
DRM panel driver for dumb panels that need at most a regulator and
a GPIO to be powered up. Optionally a backlight can be attached so
@@ -124,6 +125,18 @@ config DRM_PANEL_ILITEK_IL9322
Say Y here if you want to enable support for Ilitek IL9322
QVGA (320x240) RGB, YUV and ITU-T BT.656 panels.
+config DRM_PANEL_ILITEK_ILI9341
+ tristate "Ilitek ILI9341 240x320 QVGA panels"
+ depends on OF && SPI
+ depends on DRM_KMS_HELPER
+ depends on DRM_KMS_CMA_HELPER
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
+ help
+ Say Y here if you want to enable support for Ilitek IL9341
+ QVGA (240x320) RGB panels. support serial & parallel rgb
+ interface.
+
config DRM_PANEL_ILITEK_ILI9881C
tristate "Ilitek ILI9881C-based panels"
depends on OF
@@ -133,6 +146,15 @@ config DRM_PANEL_ILITEK_ILI9881C
Say Y if you want to enable support for panels based on the
Ilitek ILI9881c controller.
+config DRM_PANEL_INNOLUX_EJ030NA
+ tristate "Innolux EJ030NA 320x480 LCD panel"
+ depends on OF && SPI
+ select REGMAP_SPI
+ help
+ Say Y here to enable support for the Innolux/Chimei EJ030NA
+ 320x480 3.0" panel as found in the RS97 V2.1, RG300(non-ips)
+ and LDK handheld gaming consoles.
+
config DRM_PANEL_INNOLUX_P079ZCA
tristate "Innolux P079ZCA panel"
depends on OF
@@ -343,6 +365,27 @@ config DRM_PANEL_RONBO_RB070D30
Say Y here if you want to enable support for Ronbo Electronics
RB070D30 1024x600 DSI panel.
+config DRM_PANEL_SAMSUNG_ATNA33XC20
+ tristate "Samsung ATNA33XC20 eDP panel"
+ depends on OF
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on PM
+ select DRM_DP_AUX_BUS
+ help
+ DRM panel driver for the Samsung ATNA33XC20 panel. This panel can't
+ be handled by the DRM_PANEL_SIMPLE driver because its power
+ sequencing is non-standard.
+
+config DRM_PANEL_SAMSUNG_DB7430
+ tristate "Samsung DB7430-based DPI panels"
+ depends on OF && SPI && GPIOLIB
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
+ help
+ Say Y here if you want to enable support for the Samsung
+ DB7430 DPI display controller used in such devices as the
+ LMS397KF04 480x800 DPI panel.
+
config DRM_PANEL_SAMSUNG_S6D16D0
tristate "Samsung S6D16D0 DSI video mode panel"
depends on OF
@@ -377,6 +420,7 @@ config DRM_PANEL_SAMSUNG_S6E63M0_SPI
depends on SPI
depends on DRM_PANEL_SAMSUNG_S6E63M0
default DRM_PANEL_SAMSUNG_S6E63M0
+ select DRM_MIPI_DBI
help
Say Y here if you want to be able to access the Samsung
S6E63M0 panel using SPI.
@@ -553,6 +597,16 @@ config DRM_PANEL_VISIONOX_RM69299
Say Y here if you want to enable support for Visionox
RM69299 DSI Video Mode panel.
+config DRM_PANEL_WIDECHIPS_WS2401
+ tristate "Widechips WS2401 DPI panel driver"
+ depends on SPI && GPIOLIB
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
+ help
+ Say Y here if you want to enable support for the Widechips WS2401 DPI
+ 480x800 display controller used in panels such as Samsung LMS380KF01.
+ This display is used in the Samsung Galaxy Ace 2 GT-I8160 (Codina).
+
config DRM_PANEL_XINPENG_XPP055C272
tristate "Xinpeng XPP055C272 panel driver"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index cae4d976c069..c8132050bcec 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
+obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_KHADAS_TS050) += panel-khadas-ts050.o
@@ -33,6 +35,8 @@ obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
@@ -58,4 +62,5 @@ obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
+obj-$(CONFIG_DRM_PANEL_WIDECHIPS_WS2401) += panel-widechips-ws2401.o
obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
diff --git a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c
index e95bc9f60b3f..44674ebedf59 100644
--- a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c
+++ b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c
@@ -302,7 +302,7 @@ static int tm5p5_nt35596_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_EOT_PACKET |
+ MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_NO_EOT_PACKET |
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
drm_panel_init(&ctx->panel, dev, &tm5p5_nt35596_panel_funcs,
diff --git a/drivers/gpu/drm/panel/panel-dsi-cm.c b/drivers/gpu/drm/panel/panel-dsi-cm.c
index 5fbfb71ca3d9..da4a69067e18 100644
--- a/drivers/gpu/drm/panel/panel-dsi-cm.c
+++ b/drivers/gpu/drm/panel/panel-dsi-cm.c
@@ -574,7 +574,7 @@ static int dsicm_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
- MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_NO_EOT_PACKET;
dsi->hs_rate = ddata->panel_data->max_hs_rate;
dsi->lp_rate = ddata->panel_data->max_lp_rate;
diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c
index 4787f0833264..80227617a4d6 100644
--- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c
+++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c
@@ -273,7 +273,7 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 1;
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_EOT_PACKET |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, &dsi->dev, &kd35t133_funcs,
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c
new file mode 100644
index 000000000000..2c3378a259b1
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ilitek ILI9341 TFT LCD drm_panel driver.
+ *
+ * This panel can be configured to support:
+ * - 16-bit parallel RGB interface
+ * - 18-bit parallel RGB interface
+ * - 4-line serial spi interface
+ *
+ * Copyright (C) 2021 Dillon Min <dillon.minfei@gmail.com>
+ *
+ * For dbi+dpi part:
+ * Derived from drivers/drm/gpu/panel/panel-ilitek-ili9322.c
+ * the reuse of DBI abstraction part referred from Linus's patch
+ * "drm/panel: s6e63m0: Switch to DBI abstraction for SPI"
+ *
+ * For only-dbi part, copy from David's code (drm/tiny/ili9341.c)
+ * Copyright 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#define ILI9341_RGB_INTERFACE 0xb0 /* RGB Interface Signal Control */
+#define ILI9341_FRC 0xb1 /* Frame Rate Control register */
+#define ILI9341_DFC 0xb6 /* Display Function Control register */
+#define ILI9341_POWER1 0xc0 /* Power Control 1 register */
+#define ILI9341_POWER2 0xc1 /* Power Control 2 register */
+#define ILI9341_VCOM1 0xc5 /* VCOM Control 1 register */
+#define ILI9341_VCOM2 0xc7 /* VCOM Control 2 register */
+#define ILI9341_POWERA 0xcb /* Power control A register */
+#define ILI9341_POWERB 0xcf /* Power control B register */
+#define ILI9341_PGAMMA 0xe0 /* Positive Gamma Correction register */
+#define ILI9341_NGAMMA 0xe1 /* Negative Gamma Correction register */
+#define ILI9341_DTCA 0xe8 /* Driver timing control A */
+#define ILI9341_DTCB 0xea /* Driver timing control B */
+#define ILI9341_POWER_SEQ 0xed /* Power on sequence register */
+#define ILI9341_3GAMMA_EN 0xf2 /* 3 Gamma enable register */
+#define ILI9341_INTERFACE 0xf6 /* Interface control register */
+#define ILI9341_PRC 0xf7 /* Pump ratio control register */
+#define ILI9341_ETMOD 0xb7 /* Entry mode set */
+
+#define ILI9341_MADCTL_BGR BIT(3)
+#define ILI9341_MADCTL_MV BIT(5)
+#define ILI9341_MADCTL_MX BIT(6)
+#define ILI9341_MADCTL_MY BIT(7)
+
+#define ILI9341_POWER_B_LEN 3
+#define ILI9341_POWER_SEQ_LEN 4
+#define ILI9341_DTCA_LEN 3
+#define ILI9341_DTCB_LEN 2
+#define ILI9341_POWER_A_LEN 5
+#define ILI9341_DFC_1_LEN 2
+#define ILI9341_FRC_LEN 2
+#define ILI9341_VCOM_1_LEN 2
+#define ILI9341_DFC_2_LEN 4
+#define ILI9341_COLUMN_ADDR_LEN 4
+#define ILI9341_PAGE_ADDR_LEN 4
+#define ILI9341_INTERFACE_LEN 3
+#define ILI9341_PGAMMA_LEN 15
+#define ILI9341_NGAMMA_LEN 15
+#define ILI9341_CA_LEN 3
+
+#define ILI9341_PIXEL_DPI_16_BITS (BIT(6) | BIT(4))
+#define ILI9341_PIXEL_DPI_18_BITS (BIT(6) | BIT(5))
+#define ILI9341_GAMMA_CURVE_1 BIT(0)
+#define ILI9341_IF_WE_MODE BIT(0)
+#define ILI9341_IF_BIG_ENDIAN 0x00
+#define ILI9341_IF_DM_RGB BIT(2)
+#define ILI9341_IF_DM_INTERNAL 0x00
+#define ILI9341_IF_DM_VSYNC BIT(3)
+#define ILI9341_IF_RM_RGB BIT(1)
+#define ILI9341_IF_RIM_RGB 0x00
+
+#define ILI9341_COLUMN_ADDR 0x00ef
+#define ILI9341_PAGE_ADDR 0x013f
+
+#define ILI9341_RGB_EPL BIT(0)
+#define ILI9341_RGB_DPL BIT(1)
+#define ILI9341_RGB_HSPL BIT(2)
+#define ILI9341_RGB_VSPL BIT(3)
+#define ILI9341_RGB_DE_MODE BIT(6)
+#define ILI9341_RGB_DISP_PATH_MEM BIT(7)
+
+#define ILI9341_DBI_VCOMH_4P6V 0x23
+#define ILI9341_DBI_PWR_2_DEFAULT 0x10
+#define ILI9341_DBI_PRC_NORMAL 0x20
+#define ILI9341_DBI_VCOM_1_VMH_4P25V 0x3e
+#define ILI9341_DBI_VCOM_1_VML_1P5V 0x28
+#define ILI9341_DBI_VCOM_2_DEC_58 0x86
+#define ILI9341_DBI_FRC_DIVA 0x00
+#define ILI9341_DBI_FRC_RTNA 0x1b
+#define ILI9341_DBI_EMS_GAS BIT(0)
+#define ILI9341_DBI_EMS_DTS BIT(1)
+#define ILI9341_DBI_EMS_GON BIT(2)
+
+/* struct ili9341_config - the system specific ILI9341 configuration */
+struct ili9341_config {
+ u32 max_spi_speed;
+ /* mode: the drm display mode */
+ const struct drm_display_mode mode;
+ /* ca: TODO: need comments for this register */
+ u8 ca[ILI9341_CA_LEN];
+ /* power_b: TODO: need comments for this register */
+ u8 power_b[ILI9341_POWER_B_LEN];
+ /* power_seq: TODO: need comments for this register */
+ u8 power_seq[ILI9341_POWER_SEQ_LEN];
+ /* dtca: TODO: need comments for this register */
+ u8 dtca[ILI9341_DTCA_LEN];
+ /* dtcb: TODO: need comments for this register */
+ u8 dtcb[ILI9341_DTCB_LEN];
+ /* power_a: TODO: need comments for this register */
+ u8 power_a[ILI9341_POWER_A_LEN];
+ /* frc: Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
+ u8 frc[ILI9341_FRC_LEN];
+ /* prc: TODO: need comments for this register */
+ u8 prc;
+ /* dfc_1: B6h DISCTRL (Display Function Control) */
+ u8 dfc_1[ILI9341_DFC_1_LEN];
+ /* power_1: Power Control 1 (C0h) */
+ u8 power_1;
+ /* power_2: Power Control 2 (C1h) */
+ u8 power_2;
+ /* vcom_1: VCOM Control 1(C5h) */
+ u8 vcom_1[ILI9341_VCOM_1_LEN];
+ /* vcom_2: VCOM Control 2(C7h) */
+ u8 vcom_2;
+ /* address_mode: Memory Access Control (36h) */
+ u8 address_mode;
+ /* g3amma_en: TODO: need comments for this register */
+ u8 g3amma_en;
+ /* rgb_interface: RGB Interface Signal Control (B0h) */
+ u8 rgb_interface;
+ /* dfc_2: refer to dfc_1 */
+ u8 dfc_2[ILI9341_DFC_2_LEN];
+ /* column_addr: Column Address Set (2Ah) */
+ u8 column_addr[ILI9341_COLUMN_ADDR_LEN];
+ /* page_addr: Page Address Set (2Bh) */
+ u8 page_addr[ILI9341_PAGE_ADDR_LEN];
+ /* interface: Interface Control (F6h) */
+ u8 interface[ILI9341_INTERFACE_LEN];
+ /*
+ * pixel_format: This command sets the pixel format for the RGB
+ * image data used by
+ */
+ u8 pixel_format;
+ /*
+ * gamma_curve: This command is used to select the desired Gamma
+ * curve for the
+ */
+ u8 gamma_curve;
+ /* pgamma: Positive Gamma Correction (E0h) */
+ u8 pgamma[ILI9341_PGAMMA_LEN];
+ /* ngamma: Negative Gamma Correction (E1h) */
+ u8 ngamma[ILI9341_NGAMMA_LEN];
+};
+
+struct ili9341 {
+ struct device *dev;
+ const struct ili9341_config *conf;
+ struct drm_panel panel;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *dc_gpio;
+ struct mipi_dbi *dbi;
+ u32 max_spi_speed;
+ struct regulator_bulk_data supplies[3];
+};
+
+/*
+ * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller
+ */
+static const struct ili9341_config ili9341_stm32f429_disco_data = {
+ .max_spi_speed = 10000000,
+ .mode = {
+ .clock = 6100,
+ .hdisplay = 240,
+ .hsync_start = 240 + 10,/* hfp 10 */
+ .hsync_end = 240 + 10 + 10,/* hsync 10 */
+ .htotal = 240 + 10 + 10 + 20,/* hbp 20 */
+ .vdisplay = 320,
+ .vsync_start = 320 + 4,/* vfp 4 */
+ .vsync_end = 320 + 4 + 2,/* vsync 2 */
+ .vtotal = 320 + 4 + 2 + 2,/* vbp 2 */
+ .flags = 0,
+ .width_mm = 65,
+ .height_mm = 50,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .ca = {0xc3, 0x08, 0x50},
+ .power_b = {0x00, 0xc1, 0x30},
+ .power_seq = {0x64, 0x03, 0x12, 0x81},
+ .dtca = {0x85, 0x00, 0x78},
+ .power_a = {0x39, 0x2c, 0x00, 0x34, 0x02},
+ .prc = 0x20,
+ .dtcb = {0x00, 0x00},
+ /* 0x00 fosc, 0x1b 70hz */
+ .frc = {0x00, 0x1b},
+ /*
+ * 0x0a Interval scan, AGND AGND AGND AGND
+ * 0xa2 Normally white, G1 -> G320, S720 -> S1,
+ * Scan Cycle 5 frames,85ms
+ */
+ .dfc_1 = {0x0a, 0xa2},
+ /* 0x10 3.65v */
+ .power_1 = 0x10,
+ /* 0x10 AVDD=vci*2, VGH=vci*7, VGL=-vci*4 */
+ .power_2 = 0x10,
+ /* 0x45 VCOMH 4.425v, 0x15 VCOML -1.975*/
+ .vcom_1 = {0x45, 0x15},
+ /* 0x90 offset voltage, VMH-48, VML-48 */
+ .vcom_2 = 0x90,
+ /*
+ * 0xc8 Row Address Order, Column Address Order
+ * BGR 1
+ */
+ .address_mode = 0xc8,
+ .g3amma_en = 0x00,
+ /*
+ * 0xc2
+ * Display Data Path: Memory
+ * RGB: DE mode
+ * DOTCLK polarity set (data fetched at the falling time)
+ */
+ .rgb_interface = ILI9341_RGB_DISP_PATH_MEM |
+ ILI9341_RGB_DE_MODE |
+ ILI9341_RGB_DPL,
+ /*
+ * 0x0a
+ * Gate outputs in non-display area: Interval scan
+ * Determine source/VCOM output in a non-display area in the partial
+ * display mode: AGND AGND AGND AGND
+ *
+ * 0xa7
+ * Scan Cycle: 15 frames
+ * fFLM = 60Hz: 255ms
+ * Liquid crystal type: Normally white
+ * Gate Output Scan Direction: G1 -> G320
+ * Source Output Scan Direction: S720 -> S1
+ *
+ * 0x27
+ * LCD Driver Line: 320 lines
+ *
+ * 0x04
+ * PCDIV: 4
+ */
+ .dfc_2 = {0x0a, 0xa7, 0x27, 0x04},
+ /* column address: 240 */
+ .column_addr = {0x00, 0x00, (ILI9341_COLUMN_ADDR >> 4) & 0xff,
+ ILI9341_COLUMN_ADDR & 0xff},
+ /* page address: 320 */
+ .page_addr = {0x00, 0x00, (ILI9341_PAGE_ADDR >> 4) & 0xff,
+ ILI9341_PAGE_ADDR & 0xff},
+ /*
+ * Memory write control: When the transfer number of data exceeds
+ * (EC-SC+1)*(EP-SP+1), the column and page number will be
+ * reset, and the exceeding data will be written into the following
+ * column and page.
+ * Display Operation Mode: RGB Interface Mode
+ * Interface for RAM Access: RGB interface
+ * 16- bit RGB interface (1 transfer/pixel)
+ */
+ .interface = {ILI9341_IF_WE_MODE, 0x00,
+ ILI9341_IF_DM_RGB | ILI9341_IF_RM_RGB},
+ /* DPI: 16 bits / pixel */
+ .pixel_format = ILI9341_PIXEL_DPI_16_BITS,
+ /* Curve Selected: Gamma curve 1 (G2.2) */
+ .gamma_curve = ILI9341_GAMMA_CURVE_1,
+ .pgamma = {0x0f, 0x29, 0x24, 0x0c, 0x0e,
+ 0x09, 0x4e, 0x78, 0x3c, 0x09,
+ 0x13, 0x05, 0x17, 0x11, 0x00},
+ .ngamma = {0x00, 0x16, 0x1b, 0x04, 0x11,
+ 0x07, 0x31, 0x33, 0x42, 0x05,
+ 0x0c, 0x0a, 0x28, 0x2f, 0x0f},
+};
+
+static inline struct ili9341 *panel_to_ili9341(struct drm_panel *panel)
+{
+ return container_of(panel, struct ili9341, panel);
+}
+
+static void ili9341_dpi_init(struct ili9341 *ili)
+{
+ struct device *dev = (&ili->panel)->dev;
+ struct mipi_dbi *dbi = ili->dbi;
+ struct ili9341_config *cfg = (struct ili9341_config *)ili->conf;
+
+ /* Power Control */
+ mipi_dbi_command_stackbuf(dbi, 0xca, cfg->ca, ILI9341_CA_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_POWERB, cfg->power_b,
+ ILI9341_POWER_B_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_POWER_SEQ, cfg->power_seq,
+ ILI9341_POWER_SEQ_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_DTCA, cfg->dtca,
+ ILI9341_DTCA_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_POWERA, cfg->power_a,
+ ILI9341_POWER_A_LEN);
+ mipi_dbi_command(ili->dbi, ILI9341_PRC, cfg->prc);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_DTCB, cfg->dtcb,
+ ILI9341_DTCB_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_FRC, cfg->frc, ILI9341_FRC_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_1,
+ ILI9341_DFC_1_LEN);
+ mipi_dbi_command(dbi, ILI9341_POWER1, cfg->power_1);
+ mipi_dbi_command(dbi, ILI9341_POWER2, cfg->power_2);
+
+ /* VCOM */
+ mipi_dbi_command_stackbuf(dbi, ILI9341_VCOM1, cfg->vcom_1,
+ ILI9341_VCOM_1_LEN);
+ mipi_dbi_command(dbi, ILI9341_VCOM2, cfg->vcom_2);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, cfg->address_mode);
+
+ /* Gamma */
+ mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, cfg->g3amma_en);
+ mipi_dbi_command(dbi, ILI9341_RGB_INTERFACE, cfg->rgb_interface);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_2,
+ ILI9341_DFC_2_LEN);
+
+ /* Colomn address set */
+ mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
+ cfg->column_addr, ILI9341_COLUMN_ADDR_LEN);
+
+ /* Page address set */
+ mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
+ cfg->page_addr, ILI9341_PAGE_ADDR_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_INTERFACE, cfg->interface,
+ ILI9341_INTERFACE_LEN);
+
+ /* Format */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, cfg->pixel_format);
+ mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+ msleep(200);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, cfg->gamma_curve);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_PGAMMA, cfg->pgamma,
+ ILI9341_PGAMMA_LEN);
+ mipi_dbi_command_stackbuf(dbi, ILI9341_NGAMMA, cfg->ngamma,
+ ILI9341_NGAMMA_LEN);
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(200);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+ mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+
+ dev_info(dev, "Initialized display rgb interface\n");
+}
+
+static int ili9341_dpi_power_on(struct ili9341 *ili)
+{
+ struct device *dev = (&ili->panel)->dev;
+ int ret = 0;
+
+ /* Assert RESET */
+ gpiod_set_value(ili->reset_gpio, 1);
+
+ /* Enable power */
+ ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies),
+ ili->supplies);
+ if (ret < 0) {
+ dev_err(dev, "unable to enable vcc\n");
+ return ret;
+ }
+ msleep(20);
+
+ /* De-assert RESET */
+ gpiod_set_value(ili->reset_gpio, 0);
+ msleep(20);
+
+ return 0;
+}
+
+static int ili9341_dpi_power_off(struct ili9341 *ili)
+{
+ /* Assert RESET */
+ gpiod_set_value(ili->reset_gpio, 1);
+
+ /* Disable power */
+ return regulator_bulk_disable(ARRAY_SIZE(ili->supplies),
+ ili->supplies);
+}
+
+static int ili9341_dpi_disable(struct drm_panel *panel)
+{
+ struct ili9341 *ili = panel_to_ili9341(panel);
+
+ mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+ return 0;
+}
+
+static int ili9341_dpi_unprepare(struct drm_panel *panel)
+{
+ struct ili9341 *ili = panel_to_ili9341(panel);
+
+ return ili9341_dpi_power_off(ili);
+}
+
+static int ili9341_dpi_prepare(struct drm_panel *panel)
+{
+ struct ili9341 *ili = panel_to_ili9341(panel);
+ int ret;
+
+ ret = ili9341_dpi_power_on(ili);
+ if (ret < 0)
+ return ret;
+
+ ili9341_dpi_init(ili);
+
+ return ret;
+}
+
+static int ili9341_dpi_enable(struct drm_panel *panel)
+{
+ struct ili9341 *ili = panel_to_ili9341(panel);
+
+ mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_ON);
+ return 0;
+}
+
+static int ili9341_dpi_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ili9341 *ili = panel_to_ili9341(panel);
+ struct drm_device *drm = connector->dev;
+ struct drm_display_mode *mode;
+ struct drm_display_info *info;
+
+ info = &connector->display_info;
+ info->width_mm = ili->conf->mode.width_mm;
+ info->height_mm = ili->conf->mode.height_mm;
+
+ if (ili->conf->rgb_interface & ILI9341_RGB_DPL)
+ info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
+ else
+ info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+
+ if (ili->conf->rgb_interface & ILI9341_RGB_EPL)
+ info->bus_flags |= DRM_BUS_FLAG_DE_LOW;
+ else
+ info->bus_flags |= DRM_BUS_FLAG_DE_HIGH;
+
+ mode = drm_mode_duplicate(drm, &ili->conf->mode);
+ if (!mode) {
+ drm_err(drm, "bad mode or failed to add mode\n");
+ return -EINVAL;
+ }
+ drm_mode_set_name(mode);
+
+ /* Set up the polarity */
+ if (ili->conf->rgb_interface & ILI9341_RGB_HSPL)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+ if (ili->conf->rgb_interface & ILI9341_RGB_VSPL)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ mode->flags |= DRM_MODE_FLAG_NVSYNC;
+
+ drm_mode_probed_add(connector, mode);
+
+ return 1; /* Number of modes */
+}
+
+static const struct drm_panel_funcs ili9341_dpi_funcs = {
+ .disable = ili9341_dpi_disable,
+ .unprepare = ili9341_dpi_unprepare,
+ .prepare = ili9341_dpi_prepare,
+ .enable = ili9341_dpi_enable,
+ .get_modes = ili9341_dpi_get_modes,
+};
+
+static void ili9341_dbi_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *crtc_state,
+ struct drm_plane_state *plane_state)
+{
+ struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
+ struct mipi_dbi *dbi = &dbidev->dbi;
+ u8 addr_mode;
+ int ret, idx;
+
+ if (!drm_dev_enter(pipe->crtc.dev, &idx))
+ return;
+
+ ret = mipi_dbi_poweron_conditional_reset(dbidev);
+ if (ret < 0)
+ goto out_exit;
+ if (ret == 1)
+ goto out_enable;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
+
+ mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30);
+ mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
+ mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78);
+ mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
+ mipi_dbi_command(dbi, ILI9341_PRC, ILI9341_DBI_PRC_NORMAL);
+ mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00);
+
+ /* Power Control */
+ mipi_dbi_command(dbi, ILI9341_POWER1, ILI9341_DBI_VCOMH_4P6V);
+ mipi_dbi_command(dbi, ILI9341_POWER2, ILI9341_DBI_PWR_2_DEFAULT);
+ /* VCOM */
+ mipi_dbi_command(dbi, ILI9341_VCOM1, ILI9341_DBI_VCOM_1_VMH_4P25V,
+ ILI9341_DBI_VCOM_1_VML_1P5V);
+ mipi_dbi_command(dbi, ILI9341_VCOM2, ILI9341_DBI_VCOM_2_DEC_58);
+
+ /* Memory Access Control */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
+ MIPI_DCS_PIXEL_FMT_16BIT);
+
+ /* Frame Rate */
+ mipi_dbi_command(dbi, ILI9341_FRC, ILI9341_DBI_FRC_DIVA & 0x03,
+ ILI9341_DBI_FRC_RTNA & 0x1f);
+
+ /* Gamma */
+ mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, ILI9341_GAMMA_CURVE_1);
+ mipi_dbi_command(dbi, ILI9341_PGAMMA,
+ 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
+ 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
+ mipi_dbi_command(dbi, ILI9341_NGAMMA,
+ 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
+ 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
+
+ /* DDRAM */
+ mipi_dbi_command(dbi, ILI9341_ETMOD, ILI9341_DBI_EMS_GAS |
+ ILI9341_DBI_EMS_DTS |
+ ILI9341_DBI_EMS_GON);
+
+ /* Display */
+ mipi_dbi_command(dbi, ILI9341_DFC, 0x08, 0x82, 0x27, 0x00);
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(100);
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+ msleep(100);
+
+out_enable:
+ switch (dbidev->rotation) {
+ default:
+ addr_mode = ILI9341_MADCTL_MX;
+ break;
+ case 90:
+ addr_mode = ILI9341_MADCTL_MV;
+ break;
+ case 180:
+ addr_mode = ILI9341_MADCTL_MY;
+ break;
+ case 270:
+ addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
+ ILI9341_MADCTL_MX;
+ break;
+ }
+
+ addr_mode |= ILI9341_MADCTL_BGR;
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+ mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
+ drm_info(&dbidev->drm, "Initialized display serial interface\n");
+out_exit:
+ drm_dev_exit(idx);
+}
+
+static const struct drm_simple_display_pipe_funcs ili9341_dbi_funcs = {
+ .enable = ili9341_dbi_enable,
+ .disable = mipi_dbi_pipe_disable,
+ .update = mipi_dbi_pipe_update,
+ .prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
+};
+
+static const struct drm_display_mode ili9341_dbi_mode = {
+ DRM_SIMPLE_MODE(240, 320, 37, 49),
+};
+
+DEFINE_DRM_GEM_CMA_FOPS(ili9341_dbi_fops);
+
+static struct drm_driver ili9341_dbi_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .fops = &ili9341_dbi_fops,
+ DRM_GEM_CMA_DRIVER_OPS_VMAP,
+ .debugfs_init = mipi_dbi_debugfs_init,
+ .name = "ili9341",
+ .desc = "Ilitek ILI9341",
+ .date = "20210716",
+ .major = 1,
+ .minor = 0,
+};
+
+static int ili9341_dbi_probe(struct spi_device *spi, struct gpio_desc *dc,
+ struct gpio_desc *reset)
+{
+ struct device *dev = &spi->dev;
+ struct mipi_dbi_dev *dbidev;
+ struct mipi_dbi *dbi;
+ struct drm_device *drm;
+ struct regulator *vcc;
+ u32 rotation = 0;
+ int ret;
+
+ vcc = devm_regulator_get_optional(dev, "vcc");
+ if (IS_ERR(vcc))
+ dev_err(dev, "get optional vcc failed\n");
+
+ dbidev = devm_drm_dev_alloc(dev, &ili9341_dbi_driver,
+ struct mipi_dbi_dev, drm);
+ if (IS_ERR(dbidev))
+ return PTR_ERR(dbidev);
+
+ dbi = &dbidev->dbi;
+ drm = &dbidev->drm;
+ dbi->reset = reset;
+ dbidev->regulator = vcc;
+
+ drm_mode_config_init(drm);
+
+ dbidev->backlight = devm_of_find_backlight(dev);
+ if (IS_ERR(dbidev->backlight))
+ return PTR_ERR(dbidev->backlight);
+
+ device_property_read_u32(dev, "rotation", &rotation);
+
+ ret = mipi_dbi_spi_init(spi, dbi, dc);
+ if (ret)
+ return ret;
+
+ ret = mipi_dbi_dev_init(dbidev, &ili9341_dbi_funcs,
+ &ili9341_dbi_mode, rotation);
+ if (ret)
+ return ret;
+
+ drm_mode_config_reset(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, drm);
+
+ drm_fbdev_generic_setup(drm, 0);
+
+ return 0;
+}
+
+static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc,
+ struct gpio_desc *reset)
+{
+ struct device *dev = &spi->dev;
+ struct ili9341 *ili;
+ int ret;
+
+ ili = devm_kzalloc(dev, sizeof(struct ili9341), GFP_KERNEL);
+ if (!ili)
+ return -ENOMEM;
+
+ ili->dbi = devm_kzalloc(dev, sizeof(struct mipi_dbi),
+ GFP_KERNEL);
+ if (!ili->dbi)
+ return -ENOMEM;
+
+ ili->supplies[0].supply = "vci";
+ ili->supplies[1].supply = "vddi";
+ ili->supplies[2].supply = "vddi-led";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
+ ili->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dbi_spi_init(spi, ili->dbi, dc);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, ili);
+ ili->reset_gpio = reset;
+ /*
+ * Every new incarnation of this display must have a unique
+ * data entry for the system in this driver.
+ */
+ ili->conf = of_device_get_match_data(dev);
+ if (!ili->conf) {
+ dev_err(dev, "missing device configuration\n");
+ return -ENODEV;
+ }
+
+ ili->max_spi_speed = ili->conf->max_spi_speed;
+ drm_panel_init(&ili->panel, dev, &ili9341_dpi_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ drm_panel_add(&ili->panel);
+
+ return 0;
+}
+
+static int ili9341_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct gpio_desc *dc;
+ struct gpio_desc *reset;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset))
+ dev_err(dev, "Failed to get gpio 'reset'\n");
+
+ dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
+ if (IS_ERR(dc))
+ dev_err(dev, "Failed to get gpio 'dc'\n");
+
+ if (!strcmp(id->name, "sf-tc240t-9370-t"))
+ return ili9341_dpi_probe(spi, dc, reset);
+ else if (!strcmp(id->name, "yx240qv29"))
+ return ili9341_dbi_probe(spi, dc, reset);
+
+ return -1;
+}
+
+static int ili9341_remove(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct ili9341 *ili = spi_get_drvdata(spi);
+ struct drm_device *drm = spi_get_drvdata(spi);
+
+ if (!strcmp(id->name, "sf-tc240t-9370-t")) {
+ ili9341_dpi_power_off(ili);
+ drm_panel_remove(&ili->panel);
+ } else if (!strcmp(id->name, "yx240qv29")) {
+ drm_dev_unplug(drm);
+ drm_atomic_helper_shutdown(drm);
+ }
+ return 0;
+}
+
+static void ili9341_shutdown(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ if (!strcmp(id->name, "yx240qv29"))
+ drm_atomic_helper_shutdown(spi_get_drvdata(spi));
+}
+
+static const struct of_device_id ili9341_of_match[] = {
+ {
+ .compatible = "st,sf-tc240t-9370-t",
+ .data = &ili9341_stm32f429_disco_data,
+ },
+ {
+ /* porting from tiny/ili9341.c
+ * for original mipi dbi compitable
+ */
+ .compatible = "adafruit,yx240qv29",
+ .data = NULL,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ili9341_of_match);
+
+static const struct spi_device_id ili9341_id[] = {
+ { "yx240qv29", 0 },
+ { "sf-tc240t-9370-t", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ili9341_id);
+
+static struct spi_driver ili9341_driver = {
+ .probe = ili9341_probe,
+ .remove = ili9341_remove,
+ .shutdown = ili9341_shutdown,
+ .id_table = ili9341_id,
+ .driver = {
+ .name = "panel-ilitek-ili9341",
+ .of_match_table = ili9341_of_match,
+ },
+};
+module_spi_driver(ili9341_driver);
+
+MODULE_AUTHOR("Dillon Min <dillon.minfei@gmail.com>");
+MODULE_DESCRIPTION("ILI9341 LCD panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-innolux-ej030na.c b/drivers/gpu/drm/panel/panel-innolux-ej030na.c
new file mode 100644
index 000000000000..34b98f70bd22
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-innolux-ej030na.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Innolux/Chimei EJ030NA TFT 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>
+
+struct ej030na_info {
+ const struct drm_display_mode *display_modes;
+ unsigned int num_modes;
+ u16 width_mm, height_mm;
+ u32 bus_format, bus_flags;
+};
+
+struct ej030na {
+ struct drm_panel panel;
+ struct spi_device *spi;
+ struct regmap *map;
+
+ const struct ej030na_info *panel_info;
+
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+};
+
+static inline struct ej030na *to_ej030na(struct drm_panel *panel)
+{
+ return container_of(panel, struct ej030na, panel);
+}
+
+static const struct reg_sequence ej030na_init_sequence[] = {
+ { 0x05, 0x1e },
+ { 0x05, 0x5c },
+ { 0x02, 0x14 },
+ { 0x03, 0x40 },
+ { 0x04, 0x07 },
+ { 0x06, 0x12 },
+ { 0x07, 0xd2 },
+ { 0x0c, 0x06 },
+ { 0x0d, 0x40 },
+ { 0x0e, 0x40 },
+ { 0x0f, 0x40 },
+ { 0x10, 0x40 },
+ { 0x11, 0x40 },
+ { 0x2f, 0x40 },
+ { 0x5a, 0x02 },
+
+ { 0x30, 0x07 },
+ { 0x31, 0x57 },
+ { 0x32, 0x53 },
+ { 0x33, 0x77 },
+ { 0x34, 0xb8 },
+ { 0x35, 0xbd },
+ { 0x36, 0xb8 },
+ { 0x37, 0xe7 },
+ { 0x38, 0x04 },
+ { 0x39, 0xff },
+
+ { 0x40, 0x0b },
+ { 0x41, 0xb8 },
+ { 0x42, 0xab },
+ { 0x43, 0xb9 },
+ { 0x44, 0x6a },
+ { 0x45, 0x56 },
+ { 0x46, 0x61 },
+ { 0x47, 0x08 },
+ { 0x48, 0x0f },
+ { 0x49, 0x0f },
+
+ { 0x2b, 0x01 },
+};
+
+static int ej030na_prepare(struct drm_panel *panel)
+{
+ struct ej030na *priv = to_ej030na(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(50, 150);
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ usleep_range(50, 150);
+
+ err = regmap_multi_reg_write(priv->map, ej030na_init_sequence,
+ ARRAY_SIZE(ej030na_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 ej030na_unprepare(struct drm_panel *panel)
+{
+ struct ej030na *priv = to_ej030na(panel);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_disable(priv->supply);
+
+ return 0;
+}
+
+static int ej030na_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ej030na *priv = to_ej030na(panel);
+ const struct ej030na_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 ej030na_funcs = {
+ .prepare = ej030na_prepare,
+ .unprepare = ej030na_unprepare,
+ .get_modes = ej030na_get_modes,
+};
+
+static const struct regmap_config ej030na_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x5a,
+};
+
+static int ej030na_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ej030na *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, &ej030na_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, &ej030na_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 ej030na_remove(struct spi_device *spi)
+{
+ struct ej030na *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 ej030na_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 + 102,
+ .vsync_end = 480 + 102 + 9 + 9,
+ .vtotal = 480 + 102 + 9 + 9,
+ .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 + 102,
+ .vsync_end = 480 + 102 + 9,
+ .vtotal = 480 + 102 + 9 + 9,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+};
+
+static const struct ej030na_info ej030na_info = {
+ .display_modes = ej030na_modes,
+ .num_modes = ARRAY_SIZE(ej030na_modes),
+ .width_mm = 70,
+ .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 ej030na_of_match[] = {
+ { .compatible = "innolux,ej030na", .data = &ej030na_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ej030na_of_match);
+
+static struct spi_driver ej030na_driver = {
+ .driver = {
+ .name = "panel-innolux-ej030na",
+ .of_match_table = ej030na_of_match,
+ },
+ .probe = ej030na_probe,
+ .remove = ej030na_remove,
+};
+module_spi_driver(ej030na_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-khadas-ts050.c b/drivers/gpu/drm/panel/panel-khadas-ts050.c
index 8f6ac1a40c31..a3ec4cbdbf7a 100644
--- a/drivers/gpu/drm/panel/panel-khadas-ts050.c
+++ b/drivers/gpu/drm/panel/panel-khadas-ts050.c
@@ -809,7 +809,7 @@ static int khadas_ts050_panel_probe(struct mipi_dsi_device *dsi)
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 | MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
khadas_ts050 = devm_kzalloc(&dsi->dev, sizeof(*khadas_ts050),
GFP_KERNEL);
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
index ed0d5f959037..a5a414920430 100644
--- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
@@ -593,7 +593,7 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
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 | MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
DRM_MODE_CONNECTOR_DSI);
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
index 3c00e4f8f803..21e48923836d 100644
--- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
@@ -442,7 +442,7 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)
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 | MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
drm_panel_init(&ctx->panel, &dsi->dev, &ltk500hd1829_funcs,
DRM_MODE_CONNECTOR_DSI);
diff --git a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
index 45b975dee587..198493a6eb6a 100644
--- a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
+++ b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
@@ -184,7 +184,7 @@ static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_NO_EOT_PACKET;
osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL);
if (!osd101t2587)
diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
new file mode 100644
index 000000000000..221db6512859
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Panel driver for the Samsung ATNA33XC20 panel. This panel can't be handled
+ * by the DRM_PANEL_SIMPLE driver because its power sequencing is non-standard.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_dp_aux_bus.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_panel.h>
+
+struct atana33xc20_panel {
+ struct drm_panel base;
+ bool prepared;
+ bool enabled;
+ bool el3_was_on;
+
+ bool no_hpd;
+ struct gpio_desc *hpd_gpio;
+
+ struct regulator *supply;
+ struct gpio_desc *el_on3_gpio;
+
+ struct edid *edid;
+
+ ktime_t powered_off_time;
+ ktime_t powered_on_time;
+ ktime_t el_on3_off_time;
+};
+
+static inline struct atana33xc20_panel *to_atana33xc20(struct drm_panel *panel)
+{
+ return container_of(panel, struct atana33xc20_panel, base);
+}
+
+static void atana33xc20_wait(ktime_t start_ktime, unsigned int min_ms)
+{
+ ktime_t now_ktime, min_ktime;
+
+ min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms));
+ now_ktime = ktime_get();
+
+ if (ktime_before(now_ktime, min_ktime))
+ msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1);
+}
+
+static int atana33xc20_suspend(struct device *dev)
+{
+ struct atana33xc20_panel *p = dev_get_drvdata(dev);
+ int ret;
+
+ /*
+ * Note 3 (Example of power off sequence in detail) in spec
+ * specifies to wait 150 ms after deasserting EL3_ON before
+ * powering off.
+ */
+ if (p->el3_was_on)
+ atana33xc20_wait(p->el_on3_off_time, 150);
+
+ ret = regulator_disable(p->supply);
+ if (ret)
+ return ret;
+ p->powered_off_time = ktime_get();
+ p->el3_was_on = false;
+
+ return 0;
+}
+
+static int atana33xc20_resume(struct device *dev)
+{
+ struct atana33xc20_panel *p = dev_get_drvdata(dev);
+ bool hpd_asserted = false;
+ int ret;
+
+ /* T12 (Power off time) is min 500 ms */
+ atana33xc20_wait(p->powered_off_time, 500);
+
+ ret = regulator_enable(p->supply);
+ if (ret)
+ return ret;
+ p->powered_on_time = ktime_get();
+
+ /*
+ * Handle HPD. Note: if HPD is hooked up to a dedicated pin on the
+ * eDP controller then "no_hpd" will be false _and_ "hpd_gpio" will be
+ * NULL. It's up to the controller driver to wait for HPD after
+ * preparing the panel in that case.
+ */
+ if (p->no_hpd) {
+ /* T3 VCC to HPD high is max 200 ms */
+ msleep(200);
+ } else if (p->hpd_gpio) {
+ ret = readx_poll_timeout(gpiod_get_value_cansleep, p->hpd_gpio,
+ hpd_asserted, hpd_asserted,
+ 1000, 200000);
+ if (!hpd_asserted)
+ dev_warn(dev, "Timeout waiting for HPD\n");
+ }
+
+ return 0;
+}
+
+static int atana33xc20_disable(struct drm_panel *panel)
+{
+ struct atana33xc20_panel *p = to_atana33xc20(panel);
+
+ /* Disabling when already disabled is a no-op */
+ if (!p->enabled)
+ return 0;
+
+ gpiod_set_value_cansleep(p->el_on3_gpio, 0);
+ p->el_on3_off_time = ktime_get();
+ p->enabled = false;
+
+ /*
+ * Keep track of the fact that EL_ON3 was on but we haven't power
+ * cycled yet. This lets us know that "el_on3_off_time" is recent (we
+ * don't need to worry about ktime wraparounds) and also makes it
+ * obvious if we try to enable again without a power cycle (see the
+ * warning in atana33xc20_enable()).
+ */
+ p->el3_was_on = true;
+
+ /*
+ * Sleeping 20 ms here (after setting the GPIO) avoids a glitch when
+ * powering off.
+ */
+ msleep(20);
+
+ return 0;
+}
+
+static int atana33xc20_enable(struct drm_panel *panel)
+{
+ struct atana33xc20_panel *p = to_atana33xc20(panel);
+
+ /* Enabling when already enabled is a no-op */
+ if (p->enabled)
+ return 0;
+
+ /*
+ * Once EL_ON3 drops we absolutely need a power cycle before the next
+ * enable or the backlight will never come on again. The code ensures
+ * this because disable() is _always_ followed by unprepare() and
+ * unprepare() forces a suspend with pm_runtime_put_sync_suspend(),
+ * but let's track just to make sure since the requirement is so
+ * non-obvious.
+ */
+ if (WARN_ON(p->el3_was_on))
+ return -EIO;
+
+ /*
+ * Note 2 (Example of power on sequence in detail) in spec specifies
+ * to wait 400 ms after powering on before asserting EL3_on.
+ */
+ atana33xc20_wait(p->powered_on_time, 400);
+
+ gpiod_set_value_cansleep(p->el_on3_gpio, 1);
+ p->enabled = true;
+
+ return 0;
+}
+
+static int atana33xc20_unprepare(struct drm_panel *panel)
+{
+ struct atana33xc20_panel *p = to_atana33xc20(panel);
+ int ret;
+
+ /* Unpreparing when already unprepared is a no-op */
+ if (!p->prepared)
+ return 0;
+
+ /*
+ * Purposely do a put_sync, don't use autosuspend. The panel's tcon
+ * seems to sometimes crash when you stop giving it data and this is
+ * the best way to ensure it will come back.
+ *
+ * NOTE: we still want autosuspend for cases where we only turn on
+ * to get the EDID or otherwise send DP AUX commands to the panel.
+ */
+ ret = pm_runtime_put_sync_suspend(panel->dev);
+ if (ret < 0)
+ return ret;
+ p->prepared = false;
+
+ return 0;
+}
+
+static int atana33xc20_prepare(struct drm_panel *panel)
+{
+ struct atana33xc20_panel *p = to_atana33xc20(panel);
+ int ret;
+
+ /* Preparing when already prepared is a no-op */
+ if (p->prepared)
+ return 0;
+
+ ret = pm_runtime_get_sync(panel->dev);
+ if (ret < 0) {
+ pm_runtime_put_autosuspend(panel->dev);
+ return ret;
+ }
+ p->prepared = true;
+
+ return 0;
+}
+
+static int atana33xc20_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct atana33xc20_panel *p = to_atana33xc20(panel);
+ struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(panel->dev);
+ int num = 0;
+
+ pm_runtime_get_sync(panel->dev);
+
+ if (!p->edid)
+ p->edid = drm_get_edid(connector, &aux_ep->aux->ddc);
+ num = drm_add_edid_modes(connector, p->edid);
+
+ pm_runtime_mark_last_busy(panel->dev);
+ pm_runtime_put_autosuspend(panel->dev);
+
+ return num;
+}
+
+static const struct drm_panel_funcs atana33xc20_funcs = {
+ .disable = atana33xc20_disable,
+ .enable = atana33xc20_enable,
+ .unprepare = atana33xc20_unprepare,
+ .prepare = atana33xc20_prepare,
+ .get_modes = atana33xc20_get_modes,
+};
+
+static void atana33xc20_runtime_disable(void *data)
+{
+ pm_runtime_disable(data);
+}
+
+static void atana33xc20_dont_use_autosuspend(void *data)
+{
+ pm_runtime_dont_use_autosuspend(data);
+}
+
+static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep)
+{
+ struct atana33xc20_panel *panel;
+ struct device *dev = &aux_ep->dev;
+ int ret;
+
+ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+ dev_set_drvdata(dev, panel);
+
+ panel->supply = devm_regulator_get(dev, "power");
+ if (IS_ERR(panel->supply))
+ return dev_err_probe(dev, PTR_ERR(panel->supply),
+ "Failed to get power supply\n");
+
+ panel->el_on3_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(panel->el_on3_gpio))
+ return dev_err_probe(dev, PTR_ERR(panel->el_on3_gpio),
+ "Failed to get enable GPIO\n");
+
+ panel->no_hpd = of_property_read_bool(dev->of_node, "no-hpd");
+ if (!panel->no_hpd) {
+ panel->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
+ if (IS_ERR(panel->hpd_gpio))
+ return dev_err_probe(dev, PTR_ERR(panel->hpd_gpio),
+ "Failed to get HPD GPIO\n");
+ }
+
+ pm_runtime_enable(dev);
+ ret = devm_add_action_or_reset(dev, atana33xc20_runtime_disable, dev);
+ if (ret)
+ return ret;
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_add_action_or_reset(dev, atana33xc20_dont_use_autosuspend, dev);
+ if (ret)
+ return ret;
+
+ drm_panel_init(&panel->base, dev, &atana33xc20_funcs, DRM_MODE_CONNECTOR_eDP);
+
+ pm_runtime_get_sync(dev);
+ ret = drm_panel_dp_aux_backlight(&panel->base, aux_ep->aux);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register dp aux backlight\n");
+
+ drm_panel_add(&panel->base);
+
+ return 0;
+}
+
+static void atana33xc20_remove(struct dp_aux_ep_device *aux_ep)
+{
+ struct device *dev = &aux_ep->dev;
+ struct atana33xc20_panel *panel = dev_get_drvdata(dev);
+
+ drm_panel_remove(&panel->base);
+ drm_panel_disable(&panel->base);
+ drm_panel_unprepare(&panel->base);
+
+ kfree(panel->edid);
+}
+
+static void atana33xc20_shutdown(struct dp_aux_ep_device *aux_ep)
+{
+ struct device *dev = &aux_ep->dev;
+ struct atana33xc20_panel *panel = dev_get_drvdata(dev);
+
+ drm_panel_disable(&panel->base);
+ drm_panel_unprepare(&panel->base);
+}
+
+static const struct of_device_id atana33xc20_dt_match[] = {
+ { .compatible = "samsung,atna33xc20", },
+ { /* sentinal */ }
+};
+MODULE_DEVICE_TABLE(of, atana33xc20_dt_match);
+
+static const struct dev_pm_ops atana33xc20_pm_ops = {
+ SET_RUNTIME_PM_OPS(atana33xc20_suspend, atana33xc20_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct dp_aux_ep_driver atana33xc20_driver = {
+ .driver = {
+ .name = "samsung_atana33xc20",
+ .of_match_table = atana33xc20_dt_match,
+ .pm = &atana33xc20_pm_ops,
+ },
+ .probe = atana33xc20_probe,
+ .remove = atana33xc20_remove,
+ .shutdown = atana33xc20_shutdown,
+};
+
+static int __init atana33xc20_init(void)
+{
+ return dp_aux_dp_driver_register(&atana33xc20_driver);
+}
+module_init(atana33xc20_init);
+
+static void __exit atana33xc20_exit(void)
+{
+ dp_aux_dp_driver_unregister(&atana33xc20_driver);
+}
+module_exit(atana33xc20_exit);
+
+MODULE_DESCRIPTION("Samsung ATANA33XC20 Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-db7430.c b/drivers/gpu/drm/panel/panel-samsung-db7430.c
new file mode 100644
index 000000000000..ead479719f00
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-db7430.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Panel driver for the Samsung LMS397KF04 480x800 DPI RGB panel.
+ * According to the data sheet the display controller is called DB7430.
+ * Found in the Samsung Galaxy Beam GT-I8350 mobile phone.
+ * Linus Walleij <linus.walleij@linaro.org>
+ */
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <video/mipi_display.h>
+
+#define DB7430_ACCESS_PROT_OFF 0xb0
+#define DB7430_UNKNOWN_B4 0xb4
+#define DB7430_USER_SELECT 0xb5
+#define DB7430_UNKNOWN_B7 0xb7
+#define DB7430_UNKNOWN_B8 0xb8
+#define DB7430_PANEL_DRIVING 0xc0
+#define DB7430_SOURCE_CONTROL 0xc1
+#define DB7430_GATE_INTERFACE 0xc4
+#define DB7430_DISPLAY_H_TIMING 0xc5
+#define DB7430_RGB_SYNC_OPTION 0xc6
+#define DB7430_GAMMA_SET_RED 0xc8
+#define DB7430_GAMMA_SET_GREEN 0xc9
+#define DB7430_GAMMA_SET_BLUE 0xca
+#define DB7430_BIAS_CURRENT_CTRL 0xd1
+#define DB7430_DDV_CTRL 0xd2
+#define DB7430_GAMMA_CTRL_REF 0xd3
+#define DB7430_UNKNOWN_D4 0xd4
+#define DB7430_DCDC_CTRL 0xd5
+#define DB7430_VCL_CTRL 0xd6
+#define DB7430_UNKNOWN_F8 0xf8
+#define DB7430_UNKNOWN_FC 0xfc
+
+#define DATA_MASK 0x100
+
+/**
+ * struct db7430 - state container for a panel controlled by the DB7430
+ * controller
+ */
+struct db7430 {
+ /** @dev: the container device */
+ struct device *dev;
+ /** @dbi: the DBI bus abstraction handle */
+ struct mipi_dbi dbi;
+ /** @panel: the DRM panel instance for this device */
+ struct drm_panel panel;
+ /** @width: the width of this panel in mm */
+ u32 width;
+ /** @height: the height of this panel in mm */
+ u32 height;
+ /** @reset: reset GPIO line */
+ struct gpio_desc *reset;
+ /** @regulators: VCCIO and VIO supply regulators */
+ struct regulator_bulk_data regulators[2];
+};
+
+static const struct drm_display_mode db7430_480_800_mode = {
+ /*
+ * 31 ns period min (htotal*vtotal*vrefresh)/1000
+ * gives a Vrefresh of ~71 Hz.
+ */
+ .clock = 32258,
+ .hdisplay = 480,
+ .hsync_start = 480 + 10,
+ .hsync_end = 480 + 10 + 4,
+ .htotal = 480 + 10 + 4 + 40,
+ .vdisplay = 800,
+ .vsync_start = 800 + 6,
+ .vsync_end = 800 + 6 + 1,
+ .vtotal = 800 + 6 + 1 + 7,
+ .width_mm = 53,
+ .height_mm = 87,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static inline struct db7430 *to_db7430(struct drm_panel *panel)
+{
+ return container_of(panel, struct db7430, panel);
+}
+
+static int db7430_power_on(struct db7430 *db)
+{
+ struct mipi_dbi *dbi = &db->dbi;
+ int ret;
+
+ /* Power up */
+ ret = regulator_bulk_enable(ARRAY_SIZE(db->regulators),
+ db->regulators);
+ if (ret) {
+ dev_err(db->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+ msleep(50);
+
+ /* Assert reset >=1 ms */
+ gpiod_set_value_cansleep(db->reset, 1);
+ usleep_range(1000, 5000);
+ /* De-assert reset */
+ gpiod_set_value_cansleep(db->reset, 0);
+ /* Wait >= 10 ms */
+ msleep(10);
+ dev_dbg(db->dev, "de-asserted RESET\n");
+
+ /*
+ * This is set to 0x0a (RGB/BGR order + horizontal flip) in order
+ * to make the display behave normally. If this is not set the displays
+ * normal output behaviour is horizontally flipped and BGR ordered. Do
+ * it twice because the first message doesn't always "take".
+ */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+ mipi_dbi_command(dbi, DB7430_ACCESS_PROT_OFF, 0x00);
+ mipi_dbi_command(dbi, DB7430_PANEL_DRIVING, 0x28, 0x08);
+ mipi_dbi_command(dbi, DB7430_SOURCE_CONTROL,
+ 0x01, 0x30, 0x15, 0x05, 0x22);
+ mipi_dbi_command(dbi, DB7430_GATE_INTERFACE,
+ 0x10, 0x01, 0x00);
+ mipi_dbi_command(dbi, DB7430_DISPLAY_H_TIMING,
+ 0x06, 0x55, 0x03, 0x07, 0x0b,
+ 0x33, 0x00, 0x01, 0x03);
+ /*
+ * 0x00 in datasheet 0x01 in vendor code 0x00, it seems 0x01 means
+ * DE active high and 0x00 means DE active low.
+ */
+ mipi_dbi_command(dbi, DB7430_RGB_SYNC_OPTION, 0x01);
+ mipi_dbi_command(dbi, DB7430_GAMMA_SET_RED,
+ /* R positive gamma */ 0x00,
+ 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
+ 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08,
+ /* R negative gamma */ 0x00,
+ 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
+ 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08);
+ mipi_dbi_command(dbi, DB7430_GAMMA_SET_GREEN,
+ /* G positive gamma */ 0x00,
+ 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
+ 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A,
+ /* G negative gamma */ 0x00,
+ 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
+ 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A);
+ mipi_dbi_command(dbi, DB7430_GAMMA_SET_BLUE,
+ /* B positive gamma */ 0x00,
+ 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
+ 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C,
+ /* B negative gamma */ 0x00,
+ 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
+ 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C);
+ mipi_dbi_command(dbi, DB7430_BIAS_CURRENT_CTRL, 0x33, 0x13);
+ mipi_dbi_command(dbi, DB7430_DDV_CTRL, 0x11, 0x00, 0x00);
+ mipi_dbi_command(dbi, DB7430_GAMMA_CTRL_REF, 0x50, 0x50);
+ mipi_dbi_command(dbi, DB7430_DCDC_CTRL, 0x2f, 0x11, 0x1e, 0x46);
+ mipi_dbi_command(dbi, DB7430_VCL_CTRL, 0x11, 0x0a);
+
+ return 0;
+}
+
+static int db7430_power_off(struct db7430 *db)
+{
+ /* Go into RESET and disable regulators */
+ gpiod_set_value_cansleep(db->reset, 1);
+ return regulator_bulk_disable(ARRAY_SIZE(db->regulators),
+ db->regulators);
+}
+
+static int db7430_unprepare(struct drm_panel *panel)
+{
+ return db7430_power_off(to_db7430(panel));
+}
+
+static int db7430_disable(struct drm_panel *panel)
+{
+ struct db7430 *db = to_db7430(panel);
+ struct mipi_dbi *dbi = &db->dbi;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
+ msleep(25);
+ mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
+ msleep(120);
+
+ return 0;
+}
+
+static int db7430_prepare(struct drm_panel *panel)
+{
+ return db7430_power_on(to_db7430(panel));
+}
+
+static int db7430_enable(struct drm_panel *panel)
+{
+ struct db7430 *db = to_db7430(panel);
+ struct mipi_dbi *dbi = &db->dbi;
+
+ /* Exit sleep mode */
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(20);
+
+ /* NVM (non-volatile memory) load sequence */
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_D4, 0x52, 0x5e);
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_F8, 0x01, 0xf5, 0xf2, 0x71, 0x44);
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_FC, 0x00, 0x08);
+ msleep(150);
+
+ /* CABC turn on sequence (BC = backlight control) */
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_B4, 0x0f, 0x00, 0x50);
+ mipi_dbi_command(dbi, DB7430_USER_SELECT, 0x80);
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_B7, 0x24);
+ mipi_dbi_command(dbi, DB7430_UNKNOWN_B8, 0x01);
+
+ /* Turn on display */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+/**
+ * db7430_get_modes() - return the mode
+ * @panel: the panel to get the mode for
+ * @connector: reference to the central DRM connector control structure
+ */
+static int db7430_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct db7430 *db = to_db7430(panel);
+ struct drm_display_mode *mode;
+ static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ mode = drm_mode_duplicate(connector->dev, &db7430_480_800_mode);
+ if (!mode) {
+ dev_err(db->dev, "failed to add mode\n");
+ return -ENOMEM;
+ }
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.bus_flags =
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+
+ drm_mode_set_name(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs db7430_drm_funcs = {
+ .disable = db7430_disable,
+ .unprepare = db7430_unprepare,
+ .prepare = db7430_prepare,
+ .enable = db7430_enable,
+ .get_modes = db7430_get_modes,
+};
+
+static int db7430_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct db7430 *db;
+ int ret;
+
+ db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL);
+ if (!db)
+ return -ENOMEM;
+ db->dev = dev;
+
+ /*
+ * VCI is the analog voltage supply
+ * VCCIO is the digital I/O voltage supply
+ */
+ db->regulators[0].supply = "vci";
+ db->regulators[1].supply = "vccio";
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(db->regulators),
+ db->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ db->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(db->reset)) {
+ ret = PTR_ERR(db->reset);
+ return dev_err_probe(dev, ret, "no RESET GPIO\n");
+ }
+
+ ret = mipi_dbi_spi_init(spi, &db->dbi, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
+
+ drm_panel_init(&db->panel, dev, &db7430_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ /* FIXME: if no external backlight, use internal backlight */
+ ret = drm_panel_of_backlight(&db->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add backlight\n");
+
+ spi_set_drvdata(spi, db);
+
+ drm_panel_add(&db->panel);
+ dev_dbg(dev, "added panel\n");
+
+ return 0;
+}
+
+static int db7430_remove(struct spi_device *spi)
+{
+ struct db7430 *db = spi_get_drvdata(spi);
+
+ drm_panel_remove(&db->panel);
+ return 0;
+}
+
+/*
+ * The DB7430 display controller may be used in several display products,
+ * so list the different variants here and add per-variant data if needed.
+ */
+static const struct of_device_id db7430_match[] = {
+ { .compatible = "samsung,lms397kf04", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, db7430_match);
+
+static struct spi_driver db7430_driver = {
+ .probe = db7430_probe,
+ .remove = db7430_remove,
+ .driver = {
+ .name = "db7430-panel",
+ .of_match_table = db7430_match,
+ },
+};
+module_spi_driver(db7430_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Samsung DB7430 panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
index b962c817fb30..ccc8ed6fe3ae 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
@@ -446,7 +446,7 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 1;
dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
+ dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET;
ctx->supplies[0].supply = "vdd3";
ctx->supplies[1].supply = "vci";
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
index 07a48f621289..e0b1a7e354f3 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
@@ -16,7 +16,8 @@
#define MCS_GLOBAL_PARAM 0xb0
#define S6E63M0_DSI_MAX_CHUNK 15 /* CMD + 15 bytes max */
-static int s6e63m0_dsi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
+static int s6e63m0_dsi_dcs_read(struct device *dev, void *trsp,
+ const u8 cmd, u8 *data)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
int ret;
@@ -32,7 +33,8 @@ static int s6e63m0_dsi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
return 0;
}
-static int s6e63m0_dsi_dcs_write(struct device *dev, const u8 *data, size_t len)
+static int s6e63m0_dsi_dcs_write(struct device *dev, void *trsp,
+ const u8 *data, size_t len)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
const u8 *seqp = data;
@@ -99,8 +101,8 @@ static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST;
- ret = s6e63m0_probe(dev, s6e63m0_dsi_dcs_read, s6e63m0_dsi_dcs_write,
- true);
+ ret = s6e63m0_probe(dev, NULL, s6e63m0_dsi_dcs_read,
+ s6e63m0_dsi_dcs_write, true);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
index 326deb3177b6..3669cc3719ce 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
@@ -5,62 +5,38 @@
#include <linux/spi/spi.h>
#include <linux/delay.h>
+#include <drm/drm_mipi_dbi.h>
#include <drm/drm_print.h>
#include "panel-samsung-s6e63m0.h"
-#define DATA_MASK 0x100
+static const u8 s6e63m0_dbi_read_commands[] = {
+ MCS_READ_ID1,
+ MCS_READ_ID2,
+ MCS_READ_ID3,
+ 0, /* sentinel */
+};
-static int s6e63m0_spi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
+static int s6e63m0_spi_dcs_read(struct device *dev, void *trsp,
+ const u8 cmd, u8 *data)
{
- struct spi_device *spi = to_spi_device(dev);
- u16 buf[1];
- u16 rbuf[1];
+ struct mipi_dbi *dbi = trsp;
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;
+ ret = mipi_dbi_command_read(dbi, cmd, data);
+ if (ret)
+ dev_err(dev, "error on DBI read command %02x\n", cmd);
return ret;
}
-static int s6e63m0_spi_write_word(struct device *dev, u16 data)
-{
- struct spi_device *spi = to_spi_device(dev);
-
- /* 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)
+static int s6e63m0_spi_dcs_write(struct device *dev, void *trsp,
+ 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);
- }
-
- if (ret) {
- dev_err(dev, "SPI error %d writing dcs seq: %*ph\n", ret,
- (int)len, data);
- }
+ struct mipi_dbi *dbi = trsp;
+ int ret;
+ ret = mipi_dbi_command_stackbuf(dbi, data[0], (data + 1), (len - 1));
usleep_range(300, 310);
return ret;
@@ -69,18 +45,21 @@ static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len)
static int s6e63m0_spi_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
+ struct mipi_dbi *dbi;
int ret;
- spi->bits_per_word = 9;
- /* 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");
- return ret;
- }
- return s6e63m0_probe(dev, s6e63m0_spi_dcs_read, s6e63m0_spi_dcs_write,
- false);
+ dbi = devm_kzalloc(dev, sizeof(*dbi), GFP_KERNEL);
+ if (!dbi)
+ return -ENOMEM;
+
+ ret = mipi_dbi_spi_init(spi, dbi, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
+ /* Register our custom MCS read commands */
+ dbi->read_commands = s6e63m0_dbi_read_commands;
+
+ return s6e63m0_probe(dev, dbi, s6e63m0_spi_dcs_read,
+ s6e63m0_spi_dcs_write, false);
}
static int s6e63m0_spi_remove(struct spi_device *spi)
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
index 603c5dfe8768..35d72ac663d6 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
@@ -22,31 +22,6 @@
#include "panel-samsung-s6e63m0.h"
-/* Manufacturer Command Set */
-#define MCS_ELVSS_ON 0xb1
-#define MCS_TEMP_SWIRE 0xb2
-#define MCS_PENTILE_1 0xb3
-#define MCS_PENTILE_2 0xb4
-#define MCS_GAMMA_DELTA_Y_RED 0xb5
-#define MCS_GAMMA_DELTA_X_RED 0xb6
-#define MCS_GAMMA_DELTA_Y_GREEN 0xb7
-#define MCS_GAMMA_DELTA_X_GREEN 0xb8
-#define MCS_GAMMA_DELTA_Y_BLUE 0xb9
-#define MCS_GAMMA_DELTA_X_BLUE 0xba
-#define MCS_MIECTL1 0xc0
-#define MCS_BCMODE 0xc1
-#define MCS_ERROR_CHECK 0xd5
-#define MCS_READ_ID1 0xda
-#define MCS_READ_ID2 0xdb
-#define MCS_READ_ID3 0xdc
-#define MCS_LEVEL_2_KEY 0xf0
-#define MCS_MTP_KEY 0xf1
-#define MCS_DISCTL 0xf2
-#define MCS_SRCCTL 0xf6
-#define MCS_IFCTL 0xf7
-#define MCS_PANELCTL 0xf8
-#define MCS_PGAMMACTL 0xfa
-
#define S6E63M0_LCD_ID_VALUE_M2 0xA4
#define S6E63M0_LCD_ID_VALUE_SM2 0xB4
#define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
@@ -283,8 +258,9 @@ static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
struct s6e63m0 {
struct device *dev;
- int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val);
- int (*dcs_write)(struct device *dev, const u8 *data, size_t len);
+ void *transport_data;
+ int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
+ int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
struct drm_panel panel;
struct backlight_device *bl_dev;
u8 lcd_type;
@@ -340,7 +316,7 @@ static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
if (ctx->error < 0)
return;
- ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
+ ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
}
static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
@@ -348,7 +324,7 @@ static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
if (ctx->error < 0 || len == 0)
return;
- ctx->error = ctx->dcs_write(ctx->dev, data, len);
+ ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
}
#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
@@ -713,9 +689,9 @@ static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
return ret;
}
-int s6e63m0_probe(struct device *dev,
- int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
- int (*dcs_write)(struct device *dev, const u8 *data, size_t len),
+int s6e63m0_probe(struct device *dev, void *trsp,
+ int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
+ int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
bool dsi_mode)
{
struct s6e63m0 *ctx;
@@ -726,6 +702,7 @@ int s6e63m0_probe(struct device *dev,
if (!ctx)
return -ENOMEM;
+ ctx->transport_data = trsp;
ctx->dsi_mode = dsi_mode;
ctx->dcs_read = dcs_read;
ctx->dcs_write = dcs_write;
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
index c669fec91763..306605ed1117 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
@@ -3,9 +3,36 @@
#ifndef _PANEL_SAMSUNG_S6E63M0_H
#define _PANEL_SAMSUNG_S6E63M0_H
-int s6e63m0_probe(struct device *dev,
- int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
- int (*dcs_write)(struct device *dev, const u8 *data,
+/* Manufacturer Command Set */
+#define MCS_ELVSS_ON 0xb1
+#define MCS_TEMP_SWIRE 0xb2
+#define MCS_PENTILE_1 0xb3
+#define MCS_PENTILE_2 0xb4
+#define MCS_GAMMA_DELTA_Y_RED 0xb5
+#define MCS_GAMMA_DELTA_X_RED 0xb6
+#define MCS_GAMMA_DELTA_Y_GREEN 0xb7
+#define MCS_GAMMA_DELTA_X_GREEN 0xb8
+#define MCS_GAMMA_DELTA_Y_BLUE 0xb9
+#define MCS_GAMMA_DELTA_X_BLUE 0xba
+#define MCS_MIECTL1 0xc0
+#define MCS_BCMODE 0xc1
+#define MCS_ERROR_CHECK 0xd5
+#define MCS_READ_ID1 0xda
+#define MCS_READ_ID2 0xdb
+#define MCS_READ_ID3 0xdc
+#define MCS_LEVEL_2_KEY 0xf0
+#define MCS_MTP_KEY 0xf1
+#define MCS_DISCTL 0xf2
+#define MCS_SRCCTL 0xf6
+#define MCS_IFCTL 0xf7
+#define MCS_PANELCTL 0xf8
+#define MCS_PGAMMACTL 0xfa
+
+int s6e63m0_probe(struct device *dev, void *trsp,
+ int (*dcs_read)(struct device *dev, void *trsp,
+ const u8 cmd, u8 *val),
+ int (*dcs_write)(struct device *dev, void *trsp,
+ const u8 *data,
size_t len),
bool dsi_mode);
int s6e63m0_remove(struct device *dev);
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
index 527371120266..9b3599d6d2de 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
@@ -990,8 +990,8 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST
- | MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP
- | MIPI_DSI_MODE_VIDEO_HSA | MIPI_DSI_MODE_EOT_PACKET
+ | MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP
+ | MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET
| MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_AUTO_VERT;
ret = s6e8aa0_parse_dt(ctx);
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
index 16dbf0f353ed..b937e24dac8e 100644
--- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
+++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
@@ -282,7 +282,7 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_CLOCK_NON_CONTINUOUS |
- MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_NO_EOT_PACKET;
sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
if (!sharp_nt)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 1b80290c2b53..9b6c4e6c38a1 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -36,6 +36,8 @@
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_dp_aux_bus.h>
+#include <drm/drm_dp_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
@@ -185,6 +187,7 @@ struct panel_simple {
struct regulator *supply;
struct i2c_adapter *ddc;
+ struct drm_dp_aux *aux;
struct gpio_desc *enable_gpio;
struct gpio_desc *hpd_gpio;
@@ -657,7 +660,8 @@ static void panel_simple_parse_panel_timing_node(struct device *dev,
dev_err(dev, "Reject override mode: No display_timing found\n");
}
-static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
+static int panel_simple_probe(struct device *dev, const struct panel_desc *desc,
+ struct drm_dp_aux *aux)
{
struct panel_simple *panel;
struct display_timing dt;
@@ -673,6 +677,7 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
panel->enabled = false;
panel->prepared_time = 0;
panel->desc = desc;
+ panel->aux = aux;
panel->no_hpd = of_property_read_bool(dev->of_node, "no-hpd");
if (!panel->no_hpd) {
@@ -707,6 +712,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
if (!panel->ddc)
return -EPROBE_DEFER;
+ } else if (aux) {
+ panel->ddc = &aux->ddc;
}
if (desc == &panel_dpi) {
@@ -742,10 +749,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
desc->bpc != 8);
break;
case DRM_MODE_CONNECTOR_eDP:
- if (desc->bus_format == 0)
- dev_warn(dev, "Specify missing bus_format\n");
- if (desc->bpc != 6 && desc->bpc != 8)
- dev_warn(dev, "Expected bpc in {6,8} but got: %u\n", desc->bpc);
+ if (desc->bpc != 6 && desc->bpc != 8 && desc->bpc != 10)
+ dev_warn(dev, "Expected bpc in {6,8,10} but got: %u\n", desc->bpc);
break;
case DRM_MODE_CONNECTOR_DSI:
if (desc->bpc != 6 && desc->bpc != 8)
@@ -793,6 +798,15 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
if (err)
goto disable_pm_runtime;
+ if (!panel->base.backlight && panel->aux) {
+ pm_runtime_get_sync(dev);
+ err = drm_panel_dp_aux_backlight(&panel->base, panel->aux);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ if (err)
+ goto disable_pm_runtime;
+ }
+
drm_panel_add(&panel->base);
return 0;
@@ -801,7 +815,7 @@ disable_pm_runtime:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
free_ddc:
- if (panel->ddc)
+ if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
put_device(&panel->ddc->dev);
return err;
@@ -817,7 +831,7 @@ static int panel_simple_remove(struct device *dev)
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
- if (panel->ddc)
+ if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
put_device(&panel->ddc->dev);
return 0;
@@ -1080,6 +1094,36 @@ static const struct panel_desc auo_b133xtn01 = {
},
};
+static const struct drm_display_mode auo_b133han05_mode = {
+ .clock = 142600,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 58,
+ .hsync_end = 1920 + 58 + 42,
+ .htotal = 1920 + 58 + 42 + 60,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 3,
+ .vsync_end = 1080 + 3 + 5,
+ .vtotal = 1080 + 3 + 5 + 54,
+};
+
+static const struct panel_desc auo_b133han05 = {
+ .modes = &auo_b133han05_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 293,
+ .height = 165,
+ },
+ .delay = {
+ .prepare = 100,
+ .enable = 20,
+ .unprepare = 50,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DATA_MSB_TO_LSB,
+ .connector_type = DRM_MODE_CONNECTOR_eDP,
+};
+
static const struct drm_display_mode auo_b133htn01_mode = {
.clock = 150660,
.hdisplay = 1920,
@@ -1107,6 +1151,36 @@ static const struct panel_desc auo_b133htn01 = {
},
};
+static const struct drm_display_mode auo_b140han06_mode = {
+ .clock = 141000,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 16,
+ .hsync_end = 1920 + 16 + 16,
+ .htotal = 1920 + 16 + 16 + 152,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 3,
+ .vsync_end = 1080 + 3 + 14,
+ .vtotal = 1080 + 3 + 14 + 19,
+};
+
+static const struct panel_desc auo_b140han06 = {
+ .modes = &auo_b140han06_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 309,
+ .height = 174,
+ },
+ .delay = {
+ .prepare = 100,
+ .enable = 20,
+ .unprepare = 50,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DATA_MSB_TO_LSB,
+ .connector_type = DRM_MODE_CONNECTOR_eDP,
+};
+
static const struct display_timing auo_g070vvn01_timings = {
.pixelclock = { 33300000, 34209000, 45000000 },
.hactive = { 800, 800, 800 },
@@ -1179,6 +1253,8 @@ static const struct panel_desc auo_g104sn02 = {
.width = 211,
.height = 158,
},
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode auo_g121ean01_mode = {
@@ -1929,6 +2005,32 @@ static const struct panel_desc edt_et035012dm6 = {
.bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
};
+static const struct drm_display_mode edt_etm0350g0dh6_mode = {
+ .clock = 6520,
+ .hdisplay = 320,
+ .hsync_start = 320 + 20,
+ .hsync_end = 320 + 20 + 68,
+ .htotal = 320 + 20 + 68,
+ .vdisplay = 240,
+ .vsync_start = 240 + 4,
+ .vsync_end = 240 + 4 + 18,
+ .vtotal = 240 + 4 + 18,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc edt_etm0350g0dh6 = {
+ .modes = &edt_etm0350g0dh6_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 70,
+ .height = 53,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
static const struct drm_display_mode edt_etm043080dh6gp_mode = {
.clock = 10870,
.hdisplay = 480,
@@ -1980,6 +2082,9 @@ static const struct panel_desc edt_etm0430g0dh6 = {
.width = 95,
.height = 54,
},
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct drm_display_mode edt_et057090dhu_mode = {
@@ -2044,6 +2149,60 @@ static const struct panel_desc edt_etm0700g0bdh6 = {
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
+static const struct drm_display_mode edt_etmv570g2dhu_mode = {
+ .clock = 25175,
+ .hdisplay = 640,
+ .hsync_start = 640,
+ .hsync_end = 640 + 16,
+ .htotal = 640 + 16 + 30 + 114,
+ .vdisplay = 480,
+ .vsync_start = 480 + 10,
+ .vsync_end = 480 + 10 + 3,
+ .vtotal = 480 + 10 + 3 + 35,
+ .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc edt_etmv570g2dhu = {
+ .modes = &edt_etmv570g2dhu_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 115,
+ .height = 86,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
+static const struct display_timing eink_vb3300_kca_timing = {
+ .pixelclock = { 40000000, 40000000, 40000000 },
+ .hactive = { 334, 334, 334 },
+ .hfront_porch = { 1, 1, 1 },
+ .hback_porch = { 1, 1, 1 },
+ .hsync_len = { 1, 1, 1 },
+ .vactive = { 1405, 1405, 1405 },
+ .vfront_porch = { 1, 1, 1 },
+ .vback_porch = { 1, 1, 1 },
+ .vsync_len = { 1, 1, 1 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+ DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE,
+};
+
+static const struct panel_desc eink_vb3300_kca = {
+ .timings = &eink_vb3300_kca_timing,
+ .num_timings = 1,
+ .bpc = 6,
+ .size = {
+ .width = 157,
+ .height = 209,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct display_timing evervision_vgg804821_timing = {
@@ -2967,6 +3126,38 @@ static const struct panel_desc logictechno_lt170410_2whc = {
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
+static const struct drm_display_mode logictechno_lttd800480070_l6wh_rt_mode = {
+ .clock = 33000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 154,
+ .hsync_end = 800 + 154 + 3,
+ .htotal = 800 + 154 + 3 + 43,
+ .vdisplay = 480,
+ .vsync_start = 480 + 47,
+ .vsync_end = 480 + 47 + 3,
+ .vtotal = 480 + 47 + 3 + 20,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc logictechno_lttd800480070_l6wh_rt = {
+ .modes = &logictechno_lttd800480070_l6wh_rt_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 154,
+ .height = 86,
+ },
+ .delay = {
+ .prepare = 45,
+ .enable = 100,
+ .disable = 100,
+ .unprepare = 45
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
static const struct drm_display_mode mitsubishi_aa070mc01_mode = {
.clock = 30400,
.hdisplay = 800,
@@ -3033,6 +3224,37 @@ static const struct panel_desc mitsubishi_aa070mc01 = {
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
};
+static const struct display_timing multi_inno_mi1010ait_1cp_timing = {
+ .pixelclock = { 68900000, 70000000, 73400000 },
+ .hactive = { 1280, 1280, 1280 },
+ .hfront_porch = { 30, 60, 71 },
+ .hback_porch = { 30, 60, 71 },
+ .hsync_len = { 10, 10, 48 },
+ .vactive = { 800, 800, 800 },
+ .vfront_porch = { 5, 10, 10 },
+ .vback_porch = { 5, 10, 10 },
+ .vsync_len = { 5, 6, 13 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+ DISPLAY_FLAGS_DE_HIGH,
+};
+
+static const struct panel_desc multi_inno_mi1010ait_1cp = {
+ .timings = &multi_inno_mi1010ait_1cp_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+ .delay = {
+ .enable = 50,
+ .disable = 50,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct display_timing nec_nl12880bc20_05_timing = {
.pixelclock = { 67000000, 71000000, 75000000 },
.hactive = { 1280, 1280, 1280 },
@@ -3463,6 +3685,46 @@ static const struct panel_desc qd43003c0_40 = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
+static const struct drm_display_mode qishenglong_gopher2b_lcd_modes[] = {
+ { /* 60 Hz */
+ .clock = 10800,
+ .hdisplay = 480,
+ .hsync_start = 480 + 77,
+ .hsync_end = 480 + 77 + 41,
+ .htotal = 480 + 77 + 41 + 2,
+ .vdisplay = 272,
+ .vsync_start = 272 + 16,
+ .vsync_end = 272 + 16 + 10,
+ .vtotal = 272 + 16 + 10 + 2,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+ },
+ { /* 50 Hz */
+ .clock = 10800,
+ .hdisplay = 480,
+ .hsync_start = 480 + 17,
+ .hsync_end = 480 + 17 + 41,
+ .htotal = 480 + 17 + 41 + 2,
+ .vdisplay = 272,
+ .vsync_start = 272 + 116,
+ .vsync_end = 272 + 116 + 10,
+ .vtotal = 272 + 116 + 10 + 2,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+ },
+};
+
+static const struct panel_desc qishenglong_gopher2b_lcd = {
+ .modes = qishenglong_gopher2b_lcd_modes,
+ .num_modes = ARRAY_SIZE(qishenglong_gopher2b_lcd_modes),
+ .bpc = 8,
+ .size = {
+ .width = 95,
+ .height = 54,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
static const struct display_timing rocktech_rk070er9427_timing = {
.pixelclock = { 26400000, 33300000, 46800000 },
.hactive = { 800, 800, 800 },
@@ -4234,9 +4496,15 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "auo,b116xw03",
.data = &auo_b116xw03,
}, {
+ .compatible = "auo,b133han05",
+ .data = &auo_b133han05,
+ }, {
.compatible = "auo,b133htn01",
.data = &auo_b133htn01,
}, {
+ .compatible = "auo,b140han06",
+ .data = &auo_b140han06,
+ }, {
.compatible = "auo,b133xtn01",
.data = &auo_b133xtn01,
}, {
@@ -4330,6 +4598,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "edt,et035012dm6",
.data = &edt_et035012dm6,
}, {
+ .compatible = "edt,etm0350g0dh6",
+ .data = &edt_etm0350g0dh6,
+ }, {
.compatible = "edt,etm043080dh6gp",
.data = &edt_etm043080dh6gp,
}, {
@@ -4351,6 +4622,12 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "edt,etm0700g0edh6",
.data = &edt_etm0700g0bdh6,
}, {
+ .compatible = "edt,etmv570g2dhu",
+ .data = &edt_etmv570g2dhu,
+ }, {
+ .compatible = "eink,vb3300-kca",
+ .data = &eink_vb3300_kca,
+ }, {
.compatible = "evervision,vgg804821",
.data = &evervision_vgg804821,
}, {
@@ -4462,9 +4739,15 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "logictechno,lt170410-2whc",
.data = &logictechno_lt170410_2whc,
}, {
+ .compatible = "logictechno,lttd800480070-l6wh-rt",
+ .data = &logictechno_lttd800480070_l6wh_rt,
+ }, {
.compatible = "mitsubishi,aa070mc01-ca1",
.data = &mitsubishi_aa070mc01,
}, {
+ .compatible = "multi-inno,mi1010ait-1cp",
+ .data = &multi_inno_mi1010ait_1cp,
+ }, {
.compatible = "nec,nl12880bc20-05",
.data = &nec_nl12880bc20_05,
}, {
@@ -4516,6 +4799,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "qiaodian,qd43003c0-40",
.data = &qd43003c0_40,
}, {
+ .compatible = "qishenglong,gopher2b-lcd",
+ .data = &qishenglong_gopher2b_lcd,
+ }, {
.compatible = "rocktech,rk070er9427",
.data = &rocktech_rk070er9427,
}, {
@@ -4632,7 +4918,7 @@ static int panel_simple_platform_probe(struct platform_device *pdev)
if (!id)
return -ENODEV;
- return panel_simple_probe(&pdev->dev, id->data);
+ return panel_simple_probe(&pdev->dev, id->data, NULL);
}
static int panel_simple_platform_remove(struct platform_device *pdev)
@@ -4867,7 +5153,7 @@ static const struct panel_desc_dsi osd101t2045_53ts = {
},
.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_MODE_EOT_PACKET,
+ MIPI_DSI_MODE_NO_EOT_PACKET,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
};
@@ -4912,7 +5198,7 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
desc = id->data;
- err = panel_simple_probe(&dsi->dev, &desc->desc);
+ err = panel_simple_probe(&dsi->dev, &desc->desc, NULL);
if (err < 0)
return err;
@@ -4957,6 +5243,38 @@ static struct mipi_dsi_driver panel_simple_dsi_driver = {
.shutdown = panel_simple_dsi_shutdown,
};
+static int panel_simple_dp_aux_ep_probe(struct dp_aux_ep_device *aux_ep)
+{
+ const struct of_device_id *id;
+
+ id = of_match_node(platform_of_match, aux_ep->dev.of_node);
+ if (!id)
+ return -ENODEV;
+
+ return panel_simple_probe(&aux_ep->dev, id->data, aux_ep->aux);
+}
+
+static void panel_simple_dp_aux_ep_remove(struct dp_aux_ep_device *aux_ep)
+{
+ panel_simple_remove(&aux_ep->dev);
+}
+
+static void panel_simple_dp_aux_ep_shutdown(struct dp_aux_ep_device *aux_ep)
+{
+ panel_simple_shutdown(&aux_ep->dev);
+}
+
+static struct dp_aux_ep_driver panel_simple_dp_aux_ep_driver = {
+ .driver = {
+ .name = "panel-simple-dp-aux",
+ .of_match_table = platform_of_match, /* Same as platform one! */
+ .pm = &panel_simple_pm_ops,
+ },
+ .probe = panel_simple_dp_aux_ep_probe,
+ .remove = panel_simple_dp_aux_ep_remove,
+ .shutdown = panel_simple_dp_aux_ep_shutdown,
+};
+
static int __init panel_simple_init(void)
{
int err;
@@ -4965,15 +5283,25 @@ static int __init panel_simple_init(void)
if (err < 0)
return err;
+ err = dp_aux_dp_driver_register(&panel_simple_dp_aux_ep_driver);
+ if (err < 0)
+ goto err_did_platform_register;
+
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
- if (err < 0) {
- platform_driver_unregister(&panel_simple_platform_driver);
- return err;
- }
+ if (err < 0)
+ goto err_did_aux_ep_register;
}
return 0;
+
+err_did_aux_ep_register:
+ dp_aux_dp_driver_unregister(&panel_simple_dp_aux_ep_driver);
+
+err_did_platform_register:
+ platform_driver_unregister(&panel_simple_platform_driver);
+
+ return err;
}
module_init(panel_simple_init);
@@ -4982,6 +5310,7 @@ static void __exit panel_simple_exit(void)
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
mipi_dsi_driver_unregister(&panel_simple_dsi_driver);
+ dp_aux_dp_driver_unregister(&panel_simple_dp_aux_ep_driver);
platform_driver_unregister(&panel_simple_platform_driver);
}
module_exit(panel_simple_exit);
diff --git a/drivers/gpu/drm/panel/panel-sony-acx424akp.c b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
index 95659a4d15e9..9536d56a94a5 100644
--- a/drivers/gpu/drm/panel/panel-sony-acx424akp.c
+++ b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
@@ -40,7 +40,6 @@
struct acx424akp {
struct drm_panel panel;
struct device *dev;
- struct backlight_device *bl;
struct regulator *supply;
struct gpio_desc *reset_gpio;
bool video_mode;
@@ -102,6 +101,18 @@ static int acx424akp_set_brightness(struct backlight_device *bl)
u8 par;
int ret;
+ if (backlight_is_blank(bl)) {
+ /* Disable backlight */
+ par = 0x00;
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ &par, 1);
+ if (ret) {
+ dev_err(acx->dev, "failed to disable display backlight (%d)\n", ret);
+ return ret;
+ }
+ return 0;
+ }
+
/* Calculate the PWM duty cycle in n/256's */
pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1);
pwm_div = max(1,
@@ -172,6 +183,12 @@ static const struct backlight_ops acx424akp_bl_ops = {
.update_status = acx424akp_set_brightness,
};
+static const struct backlight_properties acx424akp_bl_props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 512,
+ .max_brightness = 1023,
+};
+
static int acx424akp_read_id(struct acx424akp *acx)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
@@ -310,8 +327,6 @@ static int acx424akp_prepare(struct drm_panel *panel)
}
}
- acx->bl->props.power = FB_BLANK_NORMAL;
-
return 0;
err_power_off:
@@ -323,18 +338,8 @@ static int acx424akp_unprepare(struct drm_panel *panel)
{
struct acx424akp *acx = panel_to_acx424akp(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
- u8 par;
int ret;
- /* Disable backlight */
- par = 0x00;
- ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
- &par, 1);
- if (ret) {
- dev_err(acx->dev, "failed to disable display backlight (%d)\n", ret);
- return ret;
- }
-
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret) {
dev_err(acx->dev, "failed to turn display off (%d)\n", ret);
@@ -350,36 +355,10 @@ static int acx424akp_unprepare(struct drm_panel *panel)
msleep(85);
acx424akp_power_off(acx);
- acx->bl->props.power = FB_BLANK_POWERDOWN;
-
- return 0;
-}
-
-static int acx424akp_enable(struct drm_panel *panel)
-{
- struct acx424akp *acx = panel_to_acx424akp(panel);
-
- /*
- * The backlight is on as long as the display is on
- * so no use to call backlight_enable() here.
- */
- acx->bl->props.power = FB_BLANK_UNBLANK;
return 0;
}
-static int acx424akp_disable(struct drm_panel *panel)
-{
- struct acx424akp *acx = panel_to_acx424akp(panel);
-
- /*
- * The backlight is on as long as the display is on
- * so no use to call backlight_disable() here.
- */
- acx->bl->props.power = FB_BLANK_NORMAL;
-
- return 0;
-}
static int acx424akp_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
@@ -409,10 +388,8 @@ static int acx424akp_get_modes(struct drm_panel *panel,
}
static const struct drm_panel_funcs acx424akp_drm_funcs = {
- .disable = acx424akp_disable,
.unprepare = acx424akp_unprepare,
.prepare = acx424akp_prepare,
- .enable = acx424akp_enable,
.get_modes = acx424akp_get_modes,
};
@@ -458,25 +435,18 @@ static int acx424akp_probe(struct mipi_dsi_device *dsi)
/* This asserts RESET by default */
acx->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
- if (IS_ERR(acx->reset_gpio)) {
- ret = PTR_ERR(acx->reset_gpio);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to request GPIO (%d)\n", ret);
- return ret;
- }
+ if (IS_ERR(acx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(acx->reset_gpio),
+ "failed to request GPIO\n");
drm_panel_init(&acx->panel, dev, &acx424akp_drm_funcs,
DRM_MODE_CONNECTOR_DSI);
- acx->bl = devm_backlight_device_register(dev, "acx424akp", dev, acx,
- &acx424akp_bl_ops, NULL);
- if (IS_ERR(acx->bl)) {
- dev_err(dev, "failed to register backlight device\n");
- return PTR_ERR(acx->bl);
- }
- acx->bl->props.max_brightness = 1023;
- acx->bl->props.brightness = 512;
- acx->bl->props.power = FB_BLANK_POWERDOWN;
+ acx->panel.backlight = devm_backlight_device_register(dev, "acx424akp", dev, acx,
+ &acx424akp_bl_ops, &acx424akp_bl_props);
+ if (IS_ERR(acx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(acx->panel.backlight),
+ "failed to register backlight device\n");
drm_panel_add(&acx->panel);
diff --git a/drivers/gpu/drm/panel/panel-widechips-ws2401.c b/drivers/gpu/drm/panel/panel-widechips-ws2401.c
new file mode 100644
index 000000000000..8bc976f54b80
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-widechips-ws2401.c
@@ -0,0 +1,441 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Panel driver for the WideChips WS2401 480x800 DPI RGB panel, used in
+ * the Samsung Mobile Display (SMD) LMS380KF01.
+ * Found in the Samsung Galaxy Ace 2 GT-I8160 mobile phone.
+ * Linus Walleij <linus.walleij@linaro.org>
+ * Inspired by code and know-how in the vendor driver by Gareth Phillips.
+ */
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <video/mipi_display.h>
+
+#define WS2401_RESCTL 0xb8 /* Resolution select control */
+#define WS2401_PSMPS 0xbd /* SMPS positive control */
+#define WS2401_NSMPS 0xbe /* SMPS negative control */
+#define WS2401_SMPS 0xbf
+#define WS2401_BCMODE 0xc1 /* Backlight control mode */
+#define WS2401_WRBLCTL 0xc3 /* Backlight control */
+#define WS2401_WRDISBV 0xc4 /* Write manual brightness */
+#define WS2401_WRCTRLD 0xc6 /* Write BL control */
+#define WS2401_WRMIE 0xc7 /* Write MIE mode */
+#define WS2401_READ_ID1 0xda /* Read panel ID 1 */
+#define WS2401_READ_ID2 0xdb /* Read panel ID 2 */
+#define WS2401_READ_ID3 0xdc /* Read panel ID 3 */
+#define WS2401_GAMMA_R1 0xe7 /* Gamma red 1 */
+#define WS2401_GAMMA_G1 0xe8 /* Gamma green 1 */
+#define WS2401_GAMMA_B1 0xe9 /* Gamma blue 1 */
+#define WS2401_GAMMA_R2 0xea /* Gamma red 2 */
+#define WS2401_GAMMA_G2 0xeb /* Gamma green 2 */
+#define WS2401_GAMMA_B2 0xec /* Gamma blue 2 */
+#define WS2401_PASSWD1 0xf0 /* Password command for level 2 */
+#define WS2401_DISCTL 0xf2 /* Display control */
+#define WS2401_PWRCTL 0xf3 /* Power control */
+#define WS2401_VCOMCTL 0xf4 /* VCOM control */
+#define WS2401_SRCCTL 0xf5 /* Source control */
+#define WS2401_PANELCTL 0xf6 /* Panel control */
+
+static const u8 ws2401_dbi_read_commands[] = {
+ WS2401_READ_ID1,
+ WS2401_READ_ID2,
+ WS2401_READ_ID3,
+ 0, /* sentinel */
+};
+
+/**
+ * struct ws2401 - state container for a panel controlled by the WS2401
+ * controller
+ */
+struct ws2401 {
+ /** @dev: the container device */
+ struct device *dev;
+ /** @dbi: the DBI bus abstraction handle */
+ struct mipi_dbi dbi;
+ /** @panel: the DRM panel instance for this device */
+ struct drm_panel panel;
+ /** @width: the width of this panel in mm */
+ u32 width;
+ /** @height: the height of this panel in mm */
+ u32 height;
+ /** @reset: reset GPIO line */
+ struct gpio_desc *reset;
+ /** @regulators: VCCIO and VIO supply regulators */
+ struct regulator_bulk_data regulators[2];
+ /** @internal_bl: If using internal backlight */
+ bool internal_bl;
+};
+
+static const struct drm_display_mode lms380kf01_480_800_mode = {
+ /*
+ * The vendor driver states that the "SMD panel" has a clock
+ * frequency of 49920000 Hz / 2 = 24960000 Hz.
+ */
+ .clock = 24960,
+ .hdisplay = 480,
+ .hsync_start = 480 + 8,
+ .hsync_end = 480 + 8 + 10,
+ .htotal = 480 + 8 + 10 + 8,
+ .vdisplay = 800,
+ .vsync_start = 800 + 8,
+ .vsync_end = 800 + 8 + 2,
+ .vtotal = 800 + 8 + 2 + 18,
+ .width_mm = 50,
+ .height_mm = 84,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static inline struct ws2401 *to_ws2401(struct drm_panel *panel)
+{
+ return container_of(panel, struct ws2401, panel);
+}
+
+static void ws2401_read_mtp_id(struct ws2401 *ws)
+{
+ struct mipi_dbi *dbi = &ws->dbi;
+ u8 id1, id2, id3;
+ int ret;
+
+ ret = mipi_dbi_command_read(dbi, WS2401_READ_ID1, &id1);
+ if (ret) {
+ dev_err(ws->dev, "unable to read MTP ID 1\n");
+ return;
+ }
+ ret = mipi_dbi_command_read(dbi, WS2401_READ_ID2, &id2);
+ if (ret) {
+ dev_err(ws->dev, "unable to read MTP ID 2\n");
+ return;
+ }
+ ret = mipi_dbi_command_read(dbi, WS2401_READ_ID3, &id3);
+ if (ret) {
+ dev_err(ws->dev, "unable to read MTP ID 3\n");
+ return;
+ }
+ dev_info(ws->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
+}
+
+static int ws2401_power_on(struct ws2401 *ws)
+{
+ struct mipi_dbi *dbi = &ws->dbi;
+ int ret;
+
+ /* Power up */
+ ret = regulator_bulk_enable(ARRAY_SIZE(ws->regulators),
+ ws->regulators);
+ if (ret) {
+ dev_err(ws->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+ msleep(10);
+
+ /* Assert reset >=1 ms */
+ gpiod_set_value_cansleep(ws->reset, 1);
+ usleep_range(1000, 5000);
+ /* De-assert reset */
+ gpiod_set_value_cansleep(ws->reset, 0);
+ /* Wait >= 10 ms */
+ msleep(10);
+ dev_dbg(ws->dev, "de-asserted RESET\n");
+
+ /*
+ * Exit sleep mode and initialize display - some hammering is
+ * necessary.
+ */
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(50);
+
+ /* Magic to unlock level 2 control of the display */
+ mipi_dbi_command(dbi, WS2401_PASSWD1, 0x5a, 0x5a);
+ /* Configure resolution to 480RGBx800 */
+ mipi_dbi_command(dbi, WS2401_RESCTL, 0x12);
+ /* Set addressing mode Flip V(d0), Flip H(d1) RGB/BGR(d3) */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x01);
+ /* Set pixel format: 24 bpp */
+ mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 0x70);
+ mipi_dbi_command(dbi, WS2401_SMPS, 0x00, 0x0f);
+ mipi_dbi_command(dbi, WS2401_PSMPS, 0x06, 0x03, /* DDVDH: 4.6v */
+ 0x7e, 0x03, 0x12, 0x37);
+ mipi_dbi_command(dbi, WS2401_NSMPS, 0x06, 0x03, /* DDVDH: -4.6v */
+ 0x7e, 0x02, 0x15, 0x37);
+ mipi_dbi_command(dbi, WS2401_SMPS, 0x02, 0x0f);
+ mipi_dbi_command(dbi, WS2401_PWRCTL, 0x10, 0xA9, 0x00, 0x01, 0x44,
+ 0xb4, /* VGH:16.1v, VGL:-13.8v */
+ 0x50, /* GREFP:4.2v (default) */
+ 0x50, /* GREFN:-4.2v (default) */
+ 0x00,
+ 0x44); /* VOUTL:-10v (default) */
+ mipi_dbi_command(dbi, WS2401_DISCTL, 0x01, 0x00, 0x00, 0x00, 0x14,
+ 0x16);
+ mipi_dbi_command(dbi, WS2401_VCOMCTL, 0x30, 0x53, 0x53);
+ mipi_dbi_command(dbi, WS2401_SRCCTL, 0x03, 0x0C, 0x00, 0x00, 0x00,
+ 0x01, /* 2 dot inversion */
+ 0x01, 0x06, 0x03);
+ mipi_dbi_command(dbi, WS2401_PANELCTL, 0x14, 0x00, 0x80, 0x00);
+ mipi_dbi_command(dbi, WS2401_WRMIE, 0x01);
+
+ /* Set up gamma, probably these are P-gamma and N-gamma for each color */
+ mipi_dbi_command(dbi, WS2401_GAMMA_R1, 0x00,
+ 0x5b, 0x42, 0x41, 0x3f, 0x42, 0x3d, 0x38, 0x2e,
+ 0x2b, 0x2a, 0x27, 0x22, 0x27, 0x0f, 0x00, 0x00);
+ mipi_dbi_command(dbi, WS2401_GAMMA_R2, 0x00,
+ 0x5b, 0x42, 0x41, 0x3f, 0x42, 0x3d, 0x38, 0x2e,
+ 0x2b, 0x2a, 0x27, 0x22, 0x27, 0x0f, 0x00, 0x00);
+ mipi_dbi_command(dbi, WS2401_GAMMA_G1, 0x00,
+ 0x59, 0x40, 0x3f, 0x3e, 0x41, 0x3d, 0x39, 0x2f,
+ 0x2c, 0x2b, 0x29, 0x25, 0x29, 0x19, 0x08, 0x00);
+ mipi_dbi_command(dbi, WS2401_GAMMA_G2, 0x00,
+ 0x59, 0x40, 0x3f, 0x3e, 0x41, 0x3d, 0x39, 0x2f,
+ 0x2c, 0x2b, 0x29, 0x25, 0x29, 0x19, 0x08, 0x00);
+ mipi_dbi_command(dbi, WS2401_GAMMA_B1, 0x00,
+ 0x57, 0x3b, 0x3a, 0x3b, 0x3f, 0x3b, 0x38, 0x27,
+ 0x38, 0x2a, 0x26, 0x22, 0x34, 0x0c, 0x09, 0x00);
+ mipi_dbi_command(dbi, WS2401_GAMMA_B2, 0x00,
+ 0x57, 0x3b, 0x3a, 0x3b, 0x3f, 0x3b, 0x38, 0x27,
+ 0x38, 0x2a, 0x26, 0x22, 0x34, 0x0c, 0x09, 0x00);
+
+ if (ws->internal_bl) {
+ mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x2c);
+ } else {
+ mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00);
+ /*
+ * When not using internal backlight we do not need any further
+ * L2 accesses to the panel so we close the door on our way out.
+ * Otherwise we need to leave the L2 door open.
+ */
+ mipi_dbi_command(dbi, WS2401_PASSWD1, 0xa5, 0xa5);
+ }
+
+ return 0;
+}
+
+static int ws2401_power_off(struct ws2401 *ws)
+{
+ /* Go into RESET and disable regulators */
+ gpiod_set_value_cansleep(ws->reset, 1);
+ return regulator_bulk_disable(ARRAY_SIZE(ws->regulators),
+ ws->regulators);
+}
+
+static int ws2401_unprepare(struct drm_panel *panel)
+{
+ struct ws2401 *ws = to_ws2401(panel);
+ struct mipi_dbi *dbi = &ws->dbi;
+
+ /* Make sure we disable backlight, if any */
+ if (ws->internal_bl)
+ mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00);
+ mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
+ msleep(120);
+ return ws2401_power_off(to_ws2401(panel));
+}
+
+static int ws2401_disable(struct drm_panel *panel)
+{
+ struct ws2401 *ws = to_ws2401(panel);
+ struct mipi_dbi *dbi = &ws->dbi;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
+ msleep(25);
+
+ return 0;
+}
+
+static int ws2401_prepare(struct drm_panel *panel)
+{
+ return ws2401_power_on(to_ws2401(panel));
+}
+
+static int ws2401_enable(struct drm_panel *panel)
+{
+ struct ws2401 *ws = to_ws2401(panel);
+ struct mipi_dbi *dbi = &ws->dbi;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+/**
+ * ws2401_get_modes() - return the mode
+ * @panel: the panel to get the mode for
+ * @connector: reference to the central DRM connector control structure
+ */
+static int ws2401_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ws2401 *ws = to_ws2401(panel);
+ struct drm_display_mode *mode;
+ static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ /*
+ * We just support the LMS380KF01 so far, if we implement more panels
+ * this mode, the following connector display_info settings and
+ * probably the custom DCS sequences needs to selected based on what
+ * the target panel needs.
+ */
+ mode = drm_mode_duplicate(connector->dev, &lms380kf01_480_800_mode);
+ if (!mode) {
+ dev_err(ws->dev, "failed to add mode\n");
+ return -ENOMEM;
+ }
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.bus_flags =
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &bus_format, 1);
+
+ drm_mode_set_name(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs ws2401_drm_funcs = {
+ .disable = ws2401_disable,
+ .unprepare = ws2401_unprepare,
+ .prepare = ws2401_prepare,
+ .enable = ws2401_enable,
+ .get_modes = ws2401_get_modes,
+};
+
+static int ws2401_set_brightness(struct backlight_device *bl)
+{
+ struct ws2401 *ws = bl_get_data(bl);
+ struct mipi_dbi *dbi = &ws->dbi;
+ u8 brightness = backlight_get_brightness(bl);
+
+ if (backlight_is_blank(bl)) {
+ mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00);
+ } else {
+ mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x2c);
+ mipi_dbi_command(dbi, WS2401_WRDISBV, brightness);
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops ws2401_bl_ops = {
+ .update_status = ws2401_set_brightness,
+};
+
+static const struct backlight_properties ws2401_bl_props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 120,
+ .max_brightness = U8_MAX,
+};
+
+static int ws2401_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ws2401 *ws;
+ int ret;
+
+ ws = devm_kzalloc(dev, sizeof(*ws), GFP_KERNEL);
+ if (!ws)
+ return -ENOMEM;
+ ws->dev = dev;
+
+ /*
+ * VCI is the analog voltage supply
+ * VCCIO is the digital I/O voltage supply
+ */
+ ws->regulators[0].supply = "vci";
+ ws->regulators[1].supply = "vccio";
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(ws->regulators),
+ ws->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ ws->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ws->reset)) {
+ ret = PTR_ERR(ws->reset);
+ return dev_err_probe(dev, ret, "no RESET GPIO\n");
+ }
+
+ ret = mipi_dbi_spi_init(spi, &ws->dbi, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
+ ws->dbi.read_commands = ws2401_dbi_read_commands;
+
+ ws2401_power_on(ws);
+ ws2401_read_mtp_id(ws);
+ ws2401_power_off(ws);
+
+ drm_panel_init(&ws->panel, dev, &ws2401_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ ret = drm_panel_of_backlight(&ws->panel);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get external backlight device\n");
+
+ if (!ws->panel.backlight) {
+ dev_dbg(dev, "no external backlight, using internal backlight\n");
+ ws->panel.backlight =
+ devm_backlight_device_register(dev, "ws2401", dev, ws,
+ &ws2401_bl_ops, &ws2401_bl_props);
+ if (IS_ERR(ws->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ws->panel.backlight),
+ "failed to register backlight device\n");
+ } else {
+ dev_dbg(dev, "using external backlight\n");
+ }
+
+ spi_set_drvdata(spi, ws);
+
+ drm_panel_add(&ws->panel);
+ dev_dbg(dev, "added panel\n");
+
+ return 0;
+}
+
+static int ws2401_remove(struct spi_device *spi)
+{
+ struct ws2401 *ws = spi_get_drvdata(spi);
+
+ drm_panel_remove(&ws->panel);
+ return 0;
+}
+
+/*
+ * Samsung LMS380KF01 is the one instance of this display controller that we
+ * know about, but if more are found, the controller can be parameterized
+ * here and used for other configurations.
+ */
+static const struct of_device_id ws2401_match[] = {
+ { .compatible = "samsung,lms380kf01", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ws2401_match);
+
+static struct spi_driver ws2401_driver = {
+ .probe = ws2401_probe,
+ .remove = ws2401_remove,
+ .driver = {
+ .name = "ws2401-panel",
+ .of_match_table = ws2401_match,
+ },
+};
+module_spi_driver(ws2401_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Samsung WS2401 panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
index 55172d63a922..d17aae8b71d7 100644
--- a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
+++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
@@ -311,7 +311,7 @@ static int xpp055c272_probe(struct mipi_dsi_device *dsi)
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 | MIPI_DSI_MODE_EOT_PACKET;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
DRM_MODE_CONNECTOR_DSI);