summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/panel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-25 02:19:43 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-25 02:19:43 +0300
commitb14ffae378aa1db993e62b01392e70d1e585fb23 (patch)
tree0ac179d24e8a62ec4c2732ed18d90d83da4b82d7 /drivers/gpu/drm/panel
parent52deda9551a01879b3562e7b41748e85c591f14c (diff)
parentc6e90a1c660874736bd09c1fec6312b4b4c2ff7b (diff)
downloadlinux-b14ffae378aa1db993e62b01392e70d1e585fb23.tar.xz
Merge tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "Lots of work all over, Intel improving DG2 support, amdkfd CRIU support, msm new hw support, and faster fbdev support. dma-buf: - rename dma-buf-map to iosys-map core: - move buddy allocator to core - add pci/platform init macros - improve EDID parser deep color handling - EDID timing type 7 support - add GPD Win Max quirk - add yes/no helpers to string_helpers - flatten syncobj chains - add nomodeset support to lots of drivers - improve fb-helper clipping support - add default property value interface fbdev: - improve fbdev ops speed ttm: - add a backpointer from ttm bo->ttm resource dp: - move displayport headers - add a dp helper module bridge: - anx7625 atomic support, HDCP support panel: - split out panel-lvds and lvds bindings - find panels in OF subnodes privacy: - add chromeos privacy screen support fb: - hot unplug fw fb on forced removal simpledrm: - request region instead of marking ioresource busy - add panel oreintation property udmabuf: - fix oops with 0 pages amdgpu: - power management code cleanup - Enable freesync video mode by default - RAS code cleanup - Improve VRAM access for debug using SDMA - SR-IOV rework special register access and fixes - profiling power state request ioctl - expose IP discovery via sysfs - Cyan skillfish updates - GC 10.3.7, SDMA 5.2.7, DCN 3.1.6 updates - expose benchmark tests via debugfs - add module param to disable XGMI for testing - GPU reset debugfs register dumping support amdkfd: - CRIU support - SDMA queue fixes radeon: - UVD suspend fix - iMac backlight fix i915: - minimal parallel submission for execlists - DG2-G12 subplatform added - DG2 programming workarounds - DG2 accelerated migration support - flat CCS and CCS engine support for XeHP - initial small BAR support - drop fake LMEM support - ADL-N PCH support - bigjoiner updates - introduce VMA resources and async unbinding - register definitions cleanups - multi-FBC refactoring - DG1 OPROM over SPI support - ADL-N platform enabling - opregion mailbox #5 support - DP MST ESI improvements - drm device based logging - async flip optimisation for DG2 - CPU arch abstraction fixes - improve GuC ADS init to work on aarch64 - tweak TTM LRU priority hint - GuC 69.0.3 support - remove short term execbuf pins nouveau: - higher DP/eDP bitrates - backlight fixes msm: - dpu + dp support for sc8180x - dp support for sm8350 - dpu + dsi support for qcm2290 - 10nm dsi phy tuning support - bridge support for dp encoder - gpu support for additional 7c3 SKUs ingenic: - HDMI support for JZ4780 - aux channel EDID support ast: - AST2600 support - add wide screen support - create DP/DVI connectors omapdrm: - fix implicit dma_buf fencing vc4: - add CSC + full range support - better display firmware handoff panfrost: - add initial dual-core GPU support stm: - new revision support - fb handover support mediatek: - transfer display binding document to yaml format. - add mt8195 display device binding. - allow commands to be sent during video mode. - add wait_for_event for crtc disable by cmdq. tegra: - YUV format support rcar-du: - LVDS support for M3-W+ (R8A77961) exynos: - BGR pixel format for FIMD device" * tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm: (1529 commits) drm/i915/display: Do not re-enable PSR after it was marked as not reliable drm/i915/display: Fix HPD short pulse handling for eDP drm/amdgpu: Use drm_mode_copy() drm/radeon: Use drm_mode_copy() drm/amdgpu: Use ternary operator in `vcn_v1_0_start()` drm/amdgpu: Remove pointless on stack mode copies drm/amd/pm: fix indenting in __smu_cmn_reg_print_error() drm/amdgpu/dc: fix typos in comments drm/amdgpu: fix typos in comments drm/amd/pm: fix typos in comments drm/amdgpu: Add stolen reserved memory for MI25 SRIOV. drm/amdgpu: Merge get_reserved_allocation to get_vbios_allocations. drm/amdkfd: evict svm bo worker handle error drm/amdgpu/vcn: fix vcn ring test failure in igt reload test drm/amdgpu: only allow secure submission on rings which support that drm/amdgpu: fixed the warnings reported by kernel test robot drm/amd/display: 3.2.177 drm/amd/display: [FW Promotion] Release 0.0.108.0 drm/amd/display: Add save/restore PANEL_PWRSEQ_REF_DIV2 drm/amd/display: Wait for hubp read line for Pollock ...
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r--drivers/gpu/drm/panel/Kconfig23
-rw-r--r--drivers/gpu/drm/panel/Makefile2
-rw-r--r--drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c8
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c102
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt35560.c561
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-atna33xc20.c4
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c34
-rw-r--r--drivers/gpu/drm/panel/panel-sony-acx424akp.c490
8 files changed, 709 insertions, 515 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 9989a316fe88..ddf5f38e8731 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -294,6 +294,18 @@ config DRM_PANEL_NOVATEK_NT35510
around the Novatek NT35510 display controller, such as some
Hydis panels.
+config DRM_PANEL_NOVATEK_NT35560
+ tristate "Novatek NT35560 DSI command mode panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable the Novatek NT35560 display
+ controller. This panel supports DSI in both command and video
+ mode. This supports several panels such as Sony ACX424AKM and
+ ACX424AKP.
+
config DRM_PANEL_NOVATEK_NT35950
tristate "Novatek NT35950 DSI panel"
depends on OF
@@ -594,17 +606,6 @@ config DRM_PANEL_SITRONIX_ST7789V
Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels
-config DRM_PANEL_SONY_ACX424AKP
- tristate "Sony ACX424AKP DSI command mode panel"
- depends on OF
- depends on DRM_MIPI_DSI
- depends on BACKLIGHT_CLASS_DEVICE
- select VIDEOMODE_HELPERS
- help
- Say Y here if you want to enable the Sony ACX424 display
- panel. This panel supports DSI in both command and video
- mode.
-
config DRM_PANEL_SONY_ACX565AKM
tristate "Sony ACX565AKM panel"
depends on GPIOLIB && OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index d99fbbce49d1..5740911f637c 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
@@ -60,7 +61,6 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS060T1SX01) += panel-sharp-ls060t1sx01.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
-obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
index 5fcbde789ddb..1be150ac758f 100644
--- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
+++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
@@ -86,7 +86,7 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = {
_INIT_DCS_CMD(0x0F, 0x73),
_INIT_DCS_CMD(0x95, 0xE6),
_INIT_DCS_CMD(0x96, 0xF0),
- _INIT_DCS_CMD(0x30, 0x11),
+ _INIT_DCS_CMD(0x30, 0x00),
_INIT_DCS_CMD(0x6D, 0x66),
_INIT_DCS_CMD(0x75, 0xA2),
_INIT_DCS_CMD(0x77, 0x3B),
@@ -112,17 +112,17 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = {
_INIT_DCS_CMD(0xB1, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
_INIT_DCS_CMD(0xB2, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
- _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+ _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
_INIT_DCS_CMD(0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1),
_INIT_DCS_CMD(0xB5, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
_INIT_DCS_CMD(0xB6, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
- _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+ _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
_INIT_DCS_CMD(0xB8, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1),
_INIT_DCS_CMD(0xB9, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
_INIT_DCS_CMD(0xBA, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
- _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+ _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
_INIT_DCS_CMD(0xFF, 0x24),
_INIT_DCS_CMD(0xFB, 0x01),
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 176ef0c3cc1d..f7bfcf63d48e 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -21,6 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/iopoll.h>
@@ -36,8 +37,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/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
#include <drm/drm_panel.h>
/**
@@ -222,6 +223,8 @@ struct panel_edp {
struct gpio_desc *enable_gpio;
struct gpio_desc *hpd_gpio;
+ const struct edp_panel_entry *detected_panel;
+
struct edid *edid;
struct drm_display_mode override_mode;
@@ -606,6 +609,28 @@ static int panel_edp_get_timings(struct drm_panel *panel,
return p->desc->num_timings;
}
+static int detected_panel_show(struct seq_file *s, void *data)
+{
+ struct drm_panel *panel = s->private;
+ struct panel_edp *p = to_panel_edp(panel);
+
+ if (IS_ERR(p->detected_panel))
+ seq_puts(s, "UNKNOWN\n");
+ else if (!p->detected_panel)
+ seq_puts(s, "HARDCODED\n");
+ else
+ seq_printf(s, "%s\n", p->detected_panel->name);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(detected_panel);
+
+static void panel_edp_debugfs_init(struct drm_panel *panel, struct dentry *root)
+{
+ debugfs_create_file("detected_panel", 0600, root, panel, &detected_panel_fops);
+}
+
static const struct drm_panel_funcs panel_edp_funcs = {
.disable = panel_edp_disable,
.unprepare = panel_edp_unprepare,
@@ -613,6 +638,7 @@ static const struct drm_panel_funcs panel_edp_funcs = {
.enable = panel_edp_enable,
.get_modes = panel_edp_get_modes,
.get_timings = panel_edp_get_timings,
+ .debugfs_init = panel_edp_debugfs_init,
};
#define PANEL_EDP_BOUNDS_CHECK(to_check, bounds, field) \
@@ -666,7 +692,6 @@ static const struct edp_panel_entry *find_edp_panel(u32 panel_id);
static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel)
{
- const struct edp_panel_entry *edp_panel;
struct panel_desc *desc;
u32 panel_id;
char vend[4];
@@ -705,14 +730,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel)
}
drm_edid_decode_panel_id(panel_id, vend, &product_id);
- edp_panel = find_edp_panel(panel_id);
+ panel->detected_panel = find_edp_panel(panel_id);
/*
* We're using non-optimized timings and want it really obvious that
* someone needs to add an entry to the table, so we'll do a WARN_ON
* splat.
*/
- if (WARN_ON(!edp_panel)) {
+ if (WARN_ON(!panel->detected_panel)) {
dev_warn(dev,
"Unknown panel %s %#06x, using conservative timings\n",
vend, product_id);
@@ -734,12 +759,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel)
*/
desc->delay.unprepare = 2000;
desc->delay.enable = 200;
+
+ panel->detected_panel = ERR_PTR(-EINVAL);
} else {
dev_info(dev, "Detected %s %s (%#06x)\n",
- vend, edp_panel->name, product_id);
+ vend, panel->detected_panel->name, product_id);
/* Update the delay; everything else comes from EDID */
- desc->delay = *edp_panel->delay;
+ desc->delay = *panel->detected_panel->delay;
}
ret = 0;
@@ -1605,6 +1632,47 @@ static const struct panel_desc sharp_lq123p1jx31 = {
},
};
+static const struct drm_display_mode sharp_lq140m1jw46_mode[] = {
+ {
+ .clock = 346500,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 48,
+ .hsync_end = 1920 + 48 + 32,
+ .htotal = 1920 + 48 + 32 + 80,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 3,
+ .vsync_end = 1080 + 3 + 5,
+ .vtotal = 1080 + 3 + 5 + 69,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+ }, {
+ .clock = 144370,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 48,
+ .hsync_end = 1920 + 48 + 32,
+ .htotal = 1920 + 48 + 32 + 80,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 3,
+ .vsync_end = 1080 + 3 + 5,
+ .vtotal = 1080 + 3 + 5 + 69,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+ },
+};
+
+static const struct panel_desc sharp_lq140m1jw46 = {
+ .modes = sharp_lq140m1jw46_mode,
+ .num_modes = ARRAY_SIZE(sharp_lq140m1jw46_mode),
+ .bpc = 8,
+ .size = {
+ .width = 309,
+ .height = 174,
+ },
+ .delay = {
+ .hpd_absent = 80,
+ .enable = 50,
+ .unprepare = 500,
+ },
+};
+
static const struct drm_display_mode starry_kr122ea0sra_mode = {
.clock = 147000,
.hdisplay = 1920,
@@ -1719,6 +1787,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "sharp,lq123p1jx31",
.data = &sharp_lq123p1jx31,
}, {
+ .compatible = "sharp,lq140m1jw46",
+ .data = &sharp_lq140m1jw46,
+ }, {
.compatible = "starry,kr122ea0sra",
.data = &starry_kr122ea0sra,
}, {
@@ -1745,6 +1816,19 @@ static const struct panel_delay delay_200_500_e50 = {
.enable = 50,
};
+static const struct panel_delay delay_200_500_e80_d50 = {
+ .hpd_absent = 200,
+ .unprepare = 500,
+ .enable = 80,
+ .disable = 50,
+};
+
+static const struct panel_delay delay_100_500_e200 = {
+ .hpd_absent = 100,
+ .unprepare = 500,
+ .enable = 200,
+};
+
#define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \
{ \
.name = _name, \
@@ -1768,13 +1852,17 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x082d, &boe_nv133fhm_n61.delay, "NV133FHM-N62"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"),
+ EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),
+ EDP_PANEL_ENTRY('S', 'T', 'A', 0x0100, &delay_100_500_e200, "2081116HHD028001-51D"),
+
{ /* sentinal */ }
};
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c
new file mode 100644
index 000000000000..1b6042321ea1
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt35560.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MIPI-DSI Novatek NT35560-based panel controller.
+ *
+ * Supported panels include:
+ * Sony ACX424AKM - a 480x854 AMOLED DSI panel
+ * Sony ACX424AKP - a 480x864 AMOLED DSI panel
+ *
+ * Copyright (C) Linaro Ltd. 2019-2021
+ * Author: Linus Walleij
+ * Based on code and know-how from Marcus Lorentzon
+ * Copyright (C) ST-Ericsson SA 2010
+ * Based on code and know-how from Johan Olson and Joakim Wesslen
+ * Copyright (C) Sony Ericsson Mobile Communications 2010
+ */
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define NT35560_DCS_READ_ID1 0xDA
+#define NT35560_DCS_READ_ID2 0xDB
+#define NT35560_DCS_READ_ID3 0xDC
+#define NT35560_DCS_SET_MDDI 0xAE
+
+/*
+ * Sony seems to use vendor ID 0x81
+ */
+#define DISPLAY_SONY_ACX424AKP_ID1 0x8103
+#define DISPLAY_SONY_ACX424AKP_ID2 0x811a
+#define DISPLAY_SONY_ACX424AKP_ID3 0x811b
+/*
+ * The fourth ID looks like a bug, vendor IDs begin at 0x80
+ * and panel 00 ... seems like default values.
+ */
+#define DISPLAY_SONY_ACX424AKP_ID4 0x8000
+
+struct nt35560_config {
+ const struct drm_display_mode *vid_mode;
+ const struct drm_display_mode *cmd_mode;
+};
+
+struct nt35560 {
+ const struct nt35560_config *conf;
+ struct drm_panel panel;
+ struct device *dev;
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+ bool video_mode;
+};
+
+static const struct drm_display_mode sony_acx424akp_vid_mode = {
+ .clock = 27234,
+ .hdisplay = 480,
+ .hsync_start = 480 + 15,
+ .hsync_end = 480 + 15 + 0,
+ .htotal = 480 + 15 + 0 + 15,
+ .vdisplay = 864,
+ .vsync_start = 864 + 14,
+ .vsync_end = 864 + 14 + 1,
+ .vtotal = 864 + 14 + 1 + 11,
+ .width_mm = 48,
+ .height_mm = 84,
+ .flags = DRM_MODE_FLAG_PVSYNC,
+};
+
+/*
+ * The timings are not very helpful as the display is used in
+ * command mode using the maximum HS frequency.
+ */
+static const struct drm_display_mode sony_acx424akp_cmd_mode = {
+ .clock = 35478,
+ .hdisplay = 480,
+ .hsync_start = 480 + 154,
+ .hsync_end = 480 + 154 + 16,
+ .htotal = 480 + 154 + 16 + 32,
+ .vdisplay = 864,
+ .vsync_start = 864 + 1,
+ .vsync_end = 864 + 1 + 1,
+ .vtotal = 864 + 1 + 1 + 1,
+ /*
+ * Some desired refresh rate, experiments at the maximum "pixel"
+ * clock speed (HS clock 420 MHz) yields around 117Hz.
+ */
+ .width_mm = 48,
+ .height_mm = 84,
+};
+
+static const struct nt35560_config sony_acx424akp_data = {
+ .vid_mode = &sony_acx424akp_vid_mode,
+ .cmd_mode = &sony_acx424akp_cmd_mode,
+};
+
+static const struct drm_display_mode sony_acx424akm_vid_mode = {
+ .clock = 27234,
+ .hdisplay = 480,
+ .hsync_start = 480 + 15,
+ .hsync_end = 480 + 15 + 0,
+ .htotal = 480 + 15 + 0 + 15,
+ .vdisplay = 854,
+ .vsync_start = 854 + 14,
+ .vsync_end = 854 + 14 + 1,
+ .vtotal = 854 + 14 + 1 + 11,
+ .width_mm = 46,
+ .height_mm = 82,
+ .flags = DRM_MODE_FLAG_PVSYNC,
+};
+
+/*
+ * The timings are not very helpful as the display is used in
+ * command mode using the maximum HS frequency.
+ */
+static const struct drm_display_mode sony_acx424akm_cmd_mode = {
+ .clock = 35478,
+ .hdisplay = 480,
+ .hsync_start = 480 + 154,
+ .hsync_end = 480 + 154 + 16,
+ .htotal = 480 + 154 + 16 + 32,
+ .vdisplay = 854,
+ .vsync_start = 854 + 1,
+ .vsync_end = 854 + 1 + 1,
+ .vtotal = 854 + 1 + 1 + 1,
+ .width_mm = 46,
+ .height_mm = 82,
+};
+
+static const struct nt35560_config sony_acx424akm_data = {
+ .vid_mode = &sony_acx424akm_vid_mode,
+ .cmd_mode = &sony_acx424akm_cmd_mode,
+};
+
+static inline struct nt35560 *panel_to_nt35560(struct drm_panel *panel)
+{
+ return container_of(panel, struct nt35560, panel);
+}
+
+#define FOSC 20 /* 20Mhz */
+#define SCALE_FACTOR_NS_DIV_MHZ 1000
+
+static int nt35560_set_brightness(struct backlight_device *bl)
+{
+ struct nt35560 *nt = bl_get_data(bl);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+ int period_ns = 1023;
+ int duty_ns = bl->props.brightness;
+ u8 pwm_ratio;
+ u8 pwm_div;
+ 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(nt->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,
+ ((FOSC * period_ns) / 256) /
+ SCALE_FACTOR_NS_DIV_MHZ);
+
+ /* Set up PWM dutycycle ONE byte (differs from the standard) */
+ dev_dbg(nt->dev, "calculated duty cycle %02x\n", pwm_ratio);
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+ &pwm_ratio, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to set display PWM ratio (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Sequence to write PWMDIV:
+ * address data
+ * 0xF3 0xAA CMD2 Unlock
+ * 0x00 0x01 Enter CMD2 page 0
+ * 0X7D 0x01 No reload MTP of CMD2 P1
+ * 0x22 PWMDIV
+ * 0x7F 0xAA CMD2 page 1 lock
+ */
+ par = 0xaa;
+ ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to unlock CMD 2 (%d)\n", ret);
+ return ret;
+ }
+ par = 0x01;
+ ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to enter page 1 (%d)\n", ret);
+ return ret;
+ }
+ par = 0x01;
+ ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to disable MTP reload (%d)\n", ret);
+ return ret;
+ }
+ ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to set PWM divisor (%d)\n", ret);
+ return ret;
+ }
+ par = 0xaa;
+ ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to lock CMD 2 (%d)\n", ret);
+ return ret;
+ }
+
+ /* Enable backlight */
+ par = 0x24;
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ &par, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to enable display backlight (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops nt35560_bl_ops = {
+ .update_status = nt35560_set_brightness,
+};
+
+static const struct backlight_properties nt35560_bl_props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 512,
+ .max_brightness = 1023,
+};
+
+static int nt35560_read_id(struct nt35560 *nt)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+ u8 vendor, version, panel;
+ u16 val;
+ int ret;
+
+ ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID1, &vendor, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "could not vendor ID byte\n");
+ return ret;
+ }
+ ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID2, &version, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "could not read device version byte\n");
+ return ret;
+ }
+ ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID3, &panel, 1);
+ if (ret < 0) {
+ dev_err(nt->dev, "could not read panel ID byte\n");
+ return ret;
+ }
+
+ if (vendor == 0x00) {
+ dev_err(nt->dev, "device vendor ID is zero\n");
+ return -ENODEV;
+ }
+
+ val = (vendor << 8) | panel;
+ switch (val) {
+ case DISPLAY_SONY_ACX424AKP_ID1:
+ case DISPLAY_SONY_ACX424AKP_ID2:
+ case DISPLAY_SONY_ACX424AKP_ID3:
+ case DISPLAY_SONY_ACX424AKP_ID4:
+ dev_info(nt->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n",
+ vendor, version, panel);
+ break;
+ default:
+ dev_info(nt->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n",
+ vendor, version, panel);
+ break;
+ }
+
+ return 0;
+}
+
+static int nt35560_power_on(struct nt35560 *nt)
+{
+ int ret;
+
+ ret = regulator_enable(nt->supply);
+ if (ret) {
+ dev_err(nt->dev, "failed to enable supply (%d)\n", ret);
+ return ret;
+ }
+
+ /* Assert RESET */
+ gpiod_set_value_cansleep(nt->reset_gpio, 1);
+ udelay(20);
+ /* De-assert RESET */
+ gpiod_set_value_cansleep(nt->reset_gpio, 0);
+ usleep_range(11000, 20000);
+
+ return 0;
+}
+
+static void nt35560_power_off(struct nt35560 *nt)
+{
+ /* Assert RESET */
+ gpiod_set_value_cansleep(nt->reset_gpio, 1);
+ usleep_range(11000, 20000);
+
+ regulator_disable(nt->supply);
+}
+
+static int nt35560_prepare(struct drm_panel *panel)
+{
+ struct nt35560 *nt = panel_to_nt35560(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+ const u8 mddi = 3;
+ int ret;
+
+ ret = nt35560_power_on(nt);
+ if (ret)
+ return ret;
+
+ ret = nt35560_read_id(nt);
+ if (ret) {
+ dev_err(nt->dev, "failed to read panel ID (%d)\n", ret);
+ goto err_power_off;
+ }
+
+ /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
+ ret = mipi_dsi_dcs_set_tear_on(dsi,
+ MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret) {
+ dev_err(nt->dev, "failed to enable vblank TE (%d)\n", ret);
+ goto err_power_off;
+ }
+
+ /*
+ * Set MDDI
+ *
+ * This presumably deactivates the Qualcomm MDDI interface and
+ * selects DSI, similar code is found in other drivers such as the
+ * Sharp LS043T1LE01 which makes us suspect that this panel may be
+ * using a Novatek NT35565 or similar display driver chip that shares
+ * this command. Due to the lack of documentation we cannot know for
+ * sure.
+ */
+ ret = mipi_dsi_dcs_write(dsi, NT35560_DCS_SET_MDDI,
+ &mddi, sizeof(mddi));
+ if (ret < 0) {
+ dev_err(nt->dev, "failed to set MDDI (%d)\n", ret);
+ goto err_power_off;
+ }
+
+ /* Exit sleep mode */
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret) {
+ dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret);
+ goto err_power_off;
+ }
+ msleep(140);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret) {
+ dev_err(nt->dev, "failed to turn display on (%d)\n", ret);
+ goto err_power_off;
+ }
+ if (nt->video_mode) {
+ /* In video mode turn peripheral on */
+ ret = mipi_dsi_turn_on_peripheral(dsi);
+ if (ret) {
+ dev_err(nt->dev, "failed to turn on peripheral\n");
+ goto err_power_off;
+ }
+ }
+
+ return 0;
+
+err_power_off:
+ nt35560_power_off(nt);
+ return ret;
+}
+
+static int nt35560_unprepare(struct drm_panel *panel)
+{
+ struct nt35560 *nt = panel_to_nt35560(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+ int ret;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret) {
+ dev_err(nt->dev, "failed to turn display off (%d)\n", ret);
+ return ret;
+ }
+
+ /* Enter sleep mode */
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret) {
+ dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret);
+ return ret;
+ }
+ msleep(85);
+
+ nt35560_power_off(nt);
+
+ return 0;
+}
+
+
+static int nt35560_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct nt35560 *nt = panel_to_nt35560(panel);
+ const struct nt35560_config *conf = nt->conf;
+ struct drm_display_mode *mode;
+
+ if (nt->video_mode)
+ mode = drm_mode_duplicate(connector->dev,
+ conf->vid_mode);
+ else
+ mode = drm_mode_duplicate(connector->dev,
+ conf->cmd_mode);
+ if (!mode) {
+ dev_err(panel->dev, "bad mode or failed to add mode\n");
+ return -EINVAL;
+ }
+ drm_mode_set_name(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ drm_mode_probed_add(connector, mode);
+
+ return 1; /* Number of modes */
+}
+
+static const struct drm_panel_funcs nt35560_drm_funcs = {
+ .unprepare = nt35560_unprepare,
+ .prepare = nt35560_prepare,
+ .get_modes = nt35560_get_modes,
+};
+
+static int nt35560_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct nt35560 *nt;
+ int ret;
+
+ nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL);
+ if (!nt)
+ return -ENOMEM;
+ nt->video_mode = of_property_read_bool(dev->of_node,
+ "enforce-video-mode");
+
+ mipi_dsi_set_drvdata(dsi, nt);
+ nt->dev = dev;
+
+ nt->conf = of_device_get_match_data(dev);
+ if (!nt->conf) {
+ dev_err(dev, "missing device configuration\n");
+ return -ENODEV;
+ }
+
+ dsi->lanes = 2;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ /*
+ * FIXME: these come from the ST-Ericsson vendor driver for the
+ * HREF520 and seems to reflect limitations in the PLLs on that
+ * platform, if you have the datasheet, please cross-check the
+ * actual max rates.
+ */
+ dsi->lp_rate = 19200000;
+ dsi->hs_rate = 420160000;
+
+ if (nt->video_mode)
+ /* Burst mode using event for sync */
+ dsi->mode_flags =
+ MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_VIDEO_BURST;
+ else
+ dsi->mode_flags =
+ MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ nt->supply = devm_regulator_get(dev, "vddi");
+ if (IS_ERR(nt->supply))
+ return PTR_ERR(nt->supply);
+
+ /* This asserts RESET by default */
+ nt->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(nt->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),
+ "failed to request GPIO\n");
+
+ drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt,
+ &nt35560_bl_ops, &nt35560_bl_props);
+ if (IS_ERR(nt->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(nt->panel.backlight),
+ "failed to register backlight device\n");
+
+ drm_panel_add(&nt->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ drm_panel_remove(&nt->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nt35560_remove(struct mipi_dsi_device *dsi)
+{
+ struct nt35560 *nt = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&nt->panel);
+
+ return 0;
+}
+
+static const struct of_device_id nt35560_of_match[] = {
+ {
+ .compatible = "sony,acx424akp",
+ .data = &sony_acx424akp_data,
+ },
+ {
+ .compatible = "sony,acx424akm",
+ .data = &sony_acx424akm_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nt35560_of_match);
+
+static struct mipi_dsi_driver nt35560_driver = {
+ .probe = nt35560_probe,
+ .remove = nt35560_remove,
+ .driver = {
+ .name = "panel-novatek-nt35560",
+ .of_match_table = nt35560_of_match,
+ },
+};
+module_mipi_dsi_driver(nt35560_driver);
+
+MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("MIPI-DSI Novatek NT35560 Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
index 221db6512859..20666b6217e7 100644
--- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
+++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
@@ -14,8 +14,8 @@
#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/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index b42c1d816e79..a34f4198a534 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -2526,6 +2526,36 @@ static const struct panel_desc mitsubishi_aa070mc01 = {
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
};
+static const struct display_timing multi_inno_mi0700s4t_6_timing = {
+ .pixelclock = { 29000000, 33000000, 38000000 },
+ .hactive = { 800, 800, 800 },
+ .hfront_porch = { 180, 210, 240 },
+ .hback_porch = { 16, 16, 16 },
+ .hsync_len = { 30, 30, 30 },
+ .vactive = { 480, 480, 480 },
+ .vfront_porch = { 12, 22, 32 },
+ .vback_porch = { 10, 10, 10 },
+ .vsync_len = { 13, 13, 13 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+ DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
+ DISPLAY_FLAGS_SYNC_POSEDGE,
+};
+
+static const struct panel_desc multi_inno_mi0700s4t_6 = {
+ .timings = &multi_inno_mi0700s4t_6_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 154,
+ .height = 86,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
static const struct display_timing multi_inno_mi1010ait_1cp_timing = {
.pixelclock = { 68900000, 70000000, 73400000 },
.hactive = { 1280, 1280, 1280 },
@@ -3028,6 +3058,7 @@ static const struct drm_display_mode rocktech_rk101ii01d_ct_mode = {
static const struct panel_desc rocktech_rk101ii01d_ct = {
.modes = &rocktech_rk101ii01d_ct_mode,
+ .bpc = 8,
.num_modes = 1,
.size = {
.width = 217,
@@ -3873,6 +3904,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "mitsubishi,aa070mc01-ca1",
.data = &mitsubishi_aa070mc01,
}, {
+ .compatible = "multi-inno,mi0700s4t-6",
+ .data = &multi_inno_mi0700s4t_6,
+ }, {
.compatible = "multi-inno,mi1010ait-1cp",
.data = &multi_inno_mi1010ait_1cp,
}, {
diff --git a/drivers/gpu/drm/panel/panel-sony-acx424akp.c b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
deleted file mode 100644
index 9536d56a94a5..000000000000
--- a/drivers/gpu/drm/panel/panel-sony-acx424akp.c
+++ /dev/null
@@ -1,490 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * MIPI-DSI Sony ACX424AKP panel driver. This is a 480x864
- * AMOLED panel with a command-only DSI interface.
- *
- * Copyright (C) Linaro Ltd. 2019
- * Author: Linus Walleij
- * Based on code and know-how from Marcus Lorentzon
- * Copyright (C) ST-Ericsson SA 2010
- */
-#include <linux/backlight.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-
-#include <video/mipi_display.h>
-
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_panel.h>
-
-#define ACX424_DCS_READ_ID1 0xDA
-#define ACX424_DCS_READ_ID2 0xDB
-#define ACX424_DCS_READ_ID3 0xDC
-#define ACX424_DCS_SET_MDDI 0xAE
-
-/*
- * Sony seems to use vendor ID 0x81
- */
-#define DISPLAY_SONY_ACX424AKP_ID1 0x811b
-#define DISPLAY_SONY_ACX424AKP_ID2 0x811a
-/*
- * The third ID looks like a bug, vendor IDs begin at 0x80
- * and panel 00 ... seems like default values.
- */
-#define DISPLAY_SONY_ACX424AKP_ID3 0x8000
-
-struct acx424akp {
- struct drm_panel panel;
- struct device *dev;
- struct regulator *supply;
- struct gpio_desc *reset_gpio;
- bool video_mode;
-};
-
-static const struct drm_display_mode sony_acx424akp_vid_mode = {
- .clock = 27234,
- .hdisplay = 480,
- .hsync_start = 480 + 15,
- .hsync_end = 480 + 15 + 0,
- .htotal = 480 + 15 + 0 + 15,
- .vdisplay = 864,
- .vsync_start = 864 + 14,
- .vsync_end = 864 + 14 + 1,
- .vtotal = 864 + 14 + 1 + 11,
- .width_mm = 48,
- .height_mm = 84,
- .flags = DRM_MODE_FLAG_PVSYNC,
-};
-
-/*
- * The timings are not very helpful as the display is used in
- * command mode using the maximum HS frequency.
- */
-static const struct drm_display_mode sony_acx424akp_cmd_mode = {
- .clock = 35478,
- .hdisplay = 480,
- .hsync_start = 480 + 154,
- .hsync_end = 480 + 154 + 16,
- .htotal = 480 + 154 + 16 + 32,
- .vdisplay = 864,
- .vsync_start = 864 + 1,
- .vsync_end = 864 + 1 + 1,
- .vtotal = 864 + 1 + 1 + 1,
- /*
- * Some desired refresh rate, experiments at the maximum "pixel"
- * clock speed (HS clock 420 MHz) yields around 117Hz.
- */
- .width_mm = 48,
- .height_mm = 84,
-};
-
-static inline struct acx424akp *panel_to_acx424akp(struct drm_panel *panel)
-{
- return container_of(panel, struct acx424akp, panel);
-}
-
-#define FOSC 20 /* 20Mhz */
-#define SCALE_FACTOR_NS_DIV_MHZ 1000
-
-static int acx424akp_set_brightness(struct backlight_device *bl)
-{
- struct acx424akp *acx = bl_get_data(bl);
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
- int period_ns = 1023;
- int duty_ns = bl->props.brightness;
- u8 pwm_ratio;
- u8 pwm_div;
- 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,
- ((FOSC * period_ns) / 256) /
- SCALE_FACTOR_NS_DIV_MHZ);
-
- /* Set up PWM dutycycle ONE byte (differs from the standard) */
- dev_dbg(acx->dev, "calculated duty cycle %02x\n", pwm_ratio);
- ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
- &pwm_ratio, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to set display PWM ratio (%d)\n", ret);
- return ret;
- }
-
- /*
- * Sequence to write PWMDIV:
- * address data
- * 0xF3 0xAA CMD2 Unlock
- * 0x00 0x01 Enter CMD2 page 0
- * 0X7D 0x01 No reload MTP of CMD2 P1
- * 0x22 PWMDIV
- * 0x7F 0xAA CMD2 page 1 lock
- */
- par = 0xaa;
- ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to unlock CMD 2 (%d)\n", ret);
- return ret;
- }
- par = 0x01;
- ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to enter page 1 (%d)\n", ret);
- return ret;
- }
- par = 0x01;
- ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to disable MTP reload (%d)\n", ret);
- return ret;
- }
- ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to set PWM divisor (%d)\n", ret);
- return ret;
- }
- par = 0xaa;
- ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to lock CMD 2 (%d)\n", ret);
- return ret;
- }
-
- /* Enable backlight */
- par = 0x24;
- ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
- &par, 1);
- if (ret < 0) {
- dev_err(acx->dev, "failed to enable display backlight (%d)\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-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);
- u8 vendor, version, panel;
- u16 val;
- int ret;
-
- ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID1, &vendor, 1);
- if (ret < 0) {
- dev_err(acx->dev, "could not vendor ID byte\n");
- return ret;
- }
- ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID2, &version, 1);
- if (ret < 0) {
- dev_err(acx->dev, "could not read device version byte\n");
- return ret;
- }
- ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID3, &panel, 1);
- if (ret < 0) {
- dev_err(acx->dev, "could not read panel ID byte\n");
- return ret;
- }
-
- if (vendor == 0x00) {
- dev_err(acx->dev, "device vendor ID is zero\n");
- return -ENODEV;
- }
-
- val = (vendor << 8) | panel;
- switch (val) {
- case DISPLAY_SONY_ACX424AKP_ID1:
- case DISPLAY_SONY_ACX424AKP_ID2:
- case DISPLAY_SONY_ACX424AKP_ID3:
- dev_info(acx->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n",
- vendor, version, panel);
- break;
- default:
- dev_info(acx->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n",
- vendor, version, panel);
- break;
- }
-
- return 0;
-}
-
-static int acx424akp_power_on(struct acx424akp *acx)
-{
- int ret;
-
- ret = regulator_enable(acx->supply);
- if (ret) {
- dev_err(acx->dev, "failed to enable supply (%d)\n", ret);
- return ret;
- }
-
- /* Assert RESET */
- gpiod_set_value_cansleep(acx->reset_gpio, 1);
- udelay(20);
- /* De-assert RESET */
- gpiod_set_value_cansleep(acx->reset_gpio, 0);
- usleep_range(11000, 20000);
-
- return 0;
-}
-
-static void acx424akp_power_off(struct acx424akp *acx)
-{
- /* Assert RESET */
- gpiod_set_value_cansleep(acx->reset_gpio, 1);
- usleep_range(11000, 20000);
-
- regulator_disable(acx->supply);
-}
-
-static int acx424akp_prepare(struct drm_panel *panel)
-{
- struct acx424akp *acx = panel_to_acx424akp(panel);
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
- const u8 mddi = 3;
- int ret;
-
- ret = acx424akp_power_on(acx);
- if (ret)
- return ret;
-
- ret = acx424akp_read_id(acx);
- if (ret) {
- dev_err(acx->dev, "failed to read panel ID (%d)\n", ret);
- goto err_power_off;
- }
-
- /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
- ret = mipi_dsi_dcs_set_tear_on(dsi,
- MIPI_DSI_DCS_TEAR_MODE_VBLANK);
- if (ret) {
- dev_err(acx->dev, "failed to enable vblank TE (%d)\n", ret);
- goto err_power_off;
- }
-
- /*
- * Set MDDI
- *
- * This presumably deactivates the Qualcomm MDDI interface and
- * selects DSI, similar code is found in other drivers such as the
- * Sharp LS043T1LE01 which makes us suspect that this panel may be
- * using a Novatek NT35565 or similar display driver chip that shares
- * this command. Due to the lack of documentation we cannot know for
- * sure.
- */
- ret = mipi_dsi_dcs_write(dsi, ACX424_DCS_SET_MDDI,
- &mddi, sizeof(mddi));
- if (ret < 0) {
- dev_err(acx->dev, "failed to set MDDI (%d)\n", ret);
- goto err_power_off;
- }
-
- /* Exit sleep mode */
- ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
- if (ret) {
- dev_err(acx->dev, "failed to exit sleep mode (%d)\n", ret);
- goto err_power_off;
- }
- msleep(140);
-
- ret = mipi_dsi_dcs_set_display_on(dsi);
- if (ret) {
- dev_err(acx->dev, "failed to turn display on (%d)\n", ret);
- goto err_power_off;
- }
- if (acx->video_mode) {
- /* In video mode turn peripheral on */
- ret = mipi_dsi_turn_on_peripheral(dsi);
- if (ret) {
- dev_err(acx->dev, "failed to turn on peripheral\n");
- goto err_power_off;
- }
- }
-
- return 0;
-
-err_power_off:
- acx424akp_power_off(acx);
- return ret;
-}
-
-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);
- int ret;
-
- ret = mipi_dsi_dcs_set_display_off(dsi);
- if (ret) {
- dev_err(acx->dev, "failed to turn display off (%d)\n", ret);
- return ret;
- }
-
- /* Enter sleep mode */
- ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
- if (ret) {
- dev_err(acx->dev, "failed to enter sleep mode (%d)\n", ret);
- return ret;
- }
- msleep(85);
-
- acx424akp_power_off(acx);
-
- return 0;
-}
-
-
-static int acx424akp_get_modes(struct drm_panel *panel,
- struct drm_connector *connector)
-{
- struct acx424akp *acx = panel_to_acx424akp(panel);
- struct drm_display_mode *mode;
-
- if (acx->video_mode)
- mode = drm_mode_duplicate(connector->dev,
- &sony_acx424akp_vid_mode);
- else
- mode = drm_mode_duplicate(connector->dev,
- &sony_acx424akp_cmd_mode);
- if (!mode) {
- dev_err(panel->dev, "bad mode or failed to add mode\n");
- return -EINVAL;
- }
- drm_mode_set_name(mode);
- mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
-
- drm_mode_probed_add(connector, mode);
-
- return 1; /* Number of modes */
-}
-
-static const struct drm_panel_funcs acx424akp_drm_funcs = {
- .unprepare = acx424akp_unprepare,
- .prepare = acx424akp_prepare,
- .get_modes = acx424akp_get_modes,
-};
-
-static int acx424akp_probe(struct mipi_dsi_device *dsi)
-{
- struct device *dev = &dsi->dev;
- struct acx424akp *acx;
- int ret;
-
- acx = devm_kzalloc(dev, sizeof(struct acx424akp), GFP_KERNEL);
- if (!acx)
- return -ENOMEM;
- acx->video_mode = of_property_read_bool(dev->of_node,
- "enforce-video-mode");
-
- mipi_dsi_set_drvdata(dsi, acx);
- acx->dev = dev;
-
- dsi->lanes = 2;
- dsi->format = MIPI_DSI_FMT_RGB888;
- /*
- * FIXME: these come from the ST-Ericsson vendor driver for the
- * HREF520 and seems to reflect limitations in the PLLs on that
- * platform, if you have the datasheet, please cross-check the
- * actual max rates.
- */
- dsi->lp_rate = 19200000;
- dsi->hs_rate = 420160000;
-
- if (acx->video_mode)
- /* Burst mode using event for sync */
- dsi->mode_flags =
- MIPI_DSI_MODE_VIDEO |
- MIPI_DSI_MODE_VIDEO_BURST;
- else
- dsi->mode_flags =
- MIPI_DSI_CLOCK_NON_CONTINUOUS;
-
- acx->supply = devm_regulator_get(dev, "vddi");
- if (IS_ERR(acx->supply))
- return PTR_ERR(acx->supply);
-
- /* This asserts RESET by default */
- acx->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_HIGH);
- 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->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);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_panel_remove(&acx->panel);
- return ret;
- }
-
- return 0;
-}
-
-static int acx424akp_remove(struct mipi_dsi_device *dsi)
-{
- struct acx424akp *acx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(dsi);
- drm_panel_remove(&acx->panel);
-
- return 0;
-}
-
-static const struct of_device_id acx424akp_of_match[] = {
- { .compatible = "sony,acx424akp" },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, acx424akp_of_match);
-
-static struct mipi_dsi_driver acx424akp_driver = {
- .probe = acx424akp_probe,
- .remove = acx424akp_remove,
- .driver = {
- .name = "panel-sony-acx424akp",
- .of_match_table = acx424akp_of_match,
- },
-};
-module_mipi_dsi_driver(acx424akp_driver);
-
-MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
-MODULE_DESCRIPTION("MIPI-DSI Sony acx424akp Panel Driver");
-MODULE_LICENSE("GPL v2");