kernel/0029-feat-DRM-Add-DSI-support-for-linux-6.6.patch

1904 lines
56 KiB
Diff
Raw Normal View History

2024-12-27 22:35:16 +00:00
From 33e0fddf9766468c197d775ff0bf14ca0b59733e Mon Sep 17 00:00:00 2001
2024-12-15 18:29:23 +00:00
From: lilijun <lilijun@eswincomputing.com>
Date: Wed, 22 May 2024 14:27:49 +0800
2024-12-27 22:35:16 +00:00
Subject: [PATCH 029/222] feat(DRM): Add DSI support for linux 6.6
2024-12-15 18:29:23 +00:00
Changelogs:
1. Add support for mipi dsi driver
2. Add support for 4-lanes 1080p driver
3. make menuconfig select CONFIG_ESWIN_MIPI_DSI
---
.../dts/eswin/eswin-win2030-die0-soc.dtsi | 46 +-
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 3 +-
drivers/gpu/drm/eswin/Kconfig | 8 +
drivers/gpu/drm/eswin/Makefile | 4 +-
drivers/gpu/drm/eswin/es_drv.c | 7 +
drivers/gpu/drm/eswin/es_mipi_dsi.c | 661 ++++++++++++
drivers/gpu/drm/eswin/es_mipi_dsi.h | 11 +
drivers/gpu/drm/eswin/es_panel.c | 943 ++++++++++++++++++
drivers/gpu/drm/eswin/es_panel.h | 67 ++
9 files changed, 1713 insertions(+), 37 deletions(-)
create mode 100644 drivers/gpu/drm/eswin/es_mipi_dsi.c
create mode 100644 drivers/gpu/drm/eswin/es_mipi_dsi.h
create mode 100644 drivers/gpu/drm/eswin/es_panel.c
create mode 100644 drivers/gpu/drm/eswin/es_panel.h
diff --git a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
index a9f269bd9325..70f8a9d9af3b 100644
--- a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
+++ b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
@@ -1507,7 +1507,7 @@ portd: gpio-port@3 {
reg = <3>;
};
};
-
+
pwm0: pwm@0x50818000 {
compatible = "eswin,pwm-eswin";
reg = <0x0 0x50818000 0x0 0x4000>;
@@ -1772,9 +1772,9 @@ dc_out: port {
#address-cells = <1>;
#size-cells = <0>;
- dc_out_dpi0: endpoint@0 {
+ dc_out_dsi: endpoint@0 {
reg = <0>;
- remote-endpoint = <&dsi_input0>;
+ remote-endpoint = <&dsi_input>;
};
dc_out_dpi1: endpoint@1 {
@@ -1782,10 +1782,10 @@ dc_out_dpi1: endpoint@1 {
remote-endpoint = <&vd_input>;
};
- dc_out_hdmi: endpoint@2 {
- reg = <2>;
- remote-endpoint = <&hdmi_in_dc>;
- };
+ dc_out_hdmi: endpoint@2 {
+ reg = <2>;
+ remote-endpoint = <&hdmi_in_dc>;
+ };
};
};
@@ -1802,36 +1802,13 @@ vd_input: endpoint {
dsi_output: dsi-output {
compatible = "eswin,dsi-encoder";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- /* input */
- port@0 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0>;
- dsi_input0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&dc_out_dpi0>;
- };
- };
-
- /* output */
- port@1 {
- reg = <1>;
- dsi_out:endpoint {
- remote-endpoint = <&mipi_dsi_in>;
- };
- };
- };
+ status = "disabled";
};
dsi_controller: mipi_dsi@50270000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "eswin,dw-mipi-dsi";
+ compatible = "eswin,dsi";
reg = <0x0 0x50270000 0x0 0x10000>;
clocks = <&d0_clock WIN2030_CLK_CLK_MIPI_TXESC>;
clock-names = "pclk";
@@ -1850,9 +1827,8 @@ port@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
-
- mipi_dsi_in: endpoint {
- remote-endpoint = <&dsi_out>;
+ dsi_input: endpoint {
+ remote-endpoint = <&dc_out_dsi>;
};
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 04d4a1a10698..3f3bf54f5da3 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -29,6 +29,7 @@
#include <drm/drm_print.h>
#define HWVER_131 0x31333100 /* IP version 1.31 */
+#define HWVER_30313500 0x30313500
#define DSI_VERSION 0x00
#define VERSION GENMASK(31, 8)
@@ -781,7 +782,7 @@ static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
- if (hw_version >= HWVER_131) {
+ if (hw_version >= HWVER_131 || hw_version == HWVER_30313500) {
dsi_write(dsi, DSI_PHY_TMR_CFG,
PHY_HS2LP_TIME_V131(timing.data_hs2lp) |
PHY_LP2HS_TIME_V131(timing.data_lp2hs));
diff --git a/drivers/gpu/drm/eswin/Kconfig b/drivers/gpu/drm/eswin/Kconfig
index d518575e9bb3..1ad77afba9c3 100644
--- a/drivers/gpu/drm/eswin/Kconfig
+++ b/drivers/gpu/drm/eswin/Kconfig
@@ -70,3 +70,11 @@ config DW_HDMI_HDCP2
help
Support the HDCP2 interface which is part of the Synopsys
Designware HDMI block.
+
+
+config ESWIN_MIPI_DSI
+ tristate "Eswin mipi dsi and 1080p panel driver"
+ select DRM_ESWIN
+ select DRM_DW_MIPI_DSI
+ help
+ Support mipi dsi 4-lanes panel output
diff --git a/drivers/gpu/drm/eswin/Makefile b/drivers/gpu/drm/eswin/Makefile
index 099597edf619..6d4043ca833c 100644
--- a/drivers/gpu/drm/eswin/Makefile
+++ b/drivers/gpu/drm/eswin/Makefile
@@ -20,4 +20,6 @@ es_drm-$(CONFIG_DW_HDMI_HDCP) += dw_hdmi_hdcp.o
dw_hdcp2-$(CONFIG_DW_HDMI_HDCP2) += dw_hdmi_hdcp2.o
obj-$(CONFIG_DRM_ESWIN) += es_drm.o
-obj-$(CONFIG_DW_HDMI_HDCP2) += dw_hdcp2.o
\ No newline at end of file
+obj-$(CONFIG_DW_HDMI_HDCP2) += dw_hdcp2.o
+
+obj-$(CONFIG_ESWIN_MIPI_DSI)+= es_mipi_dsi.o es_panel.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/eswin/es_drv.c b/drivers/gpu/drm/eswin/es_drv.c
index 885b8d6fa5ab..936198b1c351 100644
--- a/drivers/gpu/drm/eswin/es_drv.c
+++ b/drivers/gpu/drm/eswin/es_drv.c
@@ -35,6 +35,8 @@
#include "dw-hdmi.h"
#endif
+#include "es_mipi_dsi.h"
+
#define DRV_NAME "es_drm"
#define DRV_DESC "Eswin DRM driver"
#define DRV_DATE "20191101"
@@ -345,6 +347,11 @@ static struct platform_driver *drm_sub_drivers[] = {
#ifdef CONFIG_ESWIN_VIRTUAL_DISPLAY
&virtual_display_platform_driver,
#endif
+
+#ifdef CONFIG_ESWIN_MIPI_DSI
+ &es_mipi_dsi_driver,
+#endif
+
};
#define NUM_DRM_DRIVERS \
(sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
diff --git a/drivers/gpu/drm/eswin/es_mipi_dsi.c b/drivers/gpu/drm/eswin/es_mipi_dsi.c
new file mode 100644
index 000000000000..8aa3c69378f2
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_mipi_dsi.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <video/mipi_display.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/bridge/dw_mipi_dsi.h>
+
+#include "es_crtc.h"
+#include "es_mipi_dsi.h"
+#include "es_panel.h"
+
+#define MAX_LANE_COUNT 4
+#define PLL_INPUT_REF_CLK 20000000
+
+/* Defines the offset of the dphy register */
+#define DSI_PHY_TST_CTRL0 0xb4
+#define PHY_TESTCLK BIT(1)
+#define PHY_UNTESTCLK 0
+#define PHY_TESTCLR BIT(0)
+#define PHY_UNTESTCLR 0
+
+#define DSI_PHY_TST_CTRL1 0xb8
+#define PHY_TESTEN BIT(16)
+#define PHY_UNTESTEN 0
+#define PHY_TESTDOUT(n) (((n) & 0xff) << 8)
+#define PHY_TESTDIN(n) (((n) & 0xff) << 0)
+#define INPUT_DIVIDER(val) (val & 0x7f)
+
+#define LOW_PROGRAM_EN 0
+#define HIGH_PROGRAM_EN BIT(7)
+#define LOOP_DIV_LOW_SEL(val) (val & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val) ((val >> 5) & 0xf)
+#define PLL_LOOP_DIV_EN BIT(5)
+#define PLL_INPUT_DIV_EN BIT(4)
+
+#define LPF_RESISTORS_15_5KOHM 0x1
+#define LPF_RESISTORS_13KOHM 0x2
+#define LPF_RESISTORS_11_5KOHM 0x4
+#define LPF_RESISTORS_10_5KOHM 0x8
+#define LPF_RESISTORS_8KOHM 0x10
+#define LPF_PROGRAM_EN BIT(6)
+#define LPF_RESISTORS_SEL(val) ((val) & 0x3f)
+
+#define LOW_PROGRAM_EN 0
+#define HIGH_PROGRAM_EN BIT(7)
+#define LOOP_DIV_LOW_SEL(val) (val & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val) ((val >> 5) & 0xf)
+#define PLL_LOOP_DIV_EN BIT(5)
+#define PLL_INPUT_DIV_EN BIT(4)
+
+#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL 0x10
+#define PLL_CP_CONTROL_PLL_LOCK_BYPASS 0x11
+#define PLL_LPF_AND_CP_CONTROL 0x12
+#define PLL_INPUT_DIVIDER_RATIO 0x17
+#define PLL_LOOP_DIVIDER_RATIO 0x18
+#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL 0x19
+
+#define DSI_TO_CNT_CFG 0x78
+#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p) ((p) & 0xffff)
+
+struct hstt {
+ unsigned int maxfreq;
+ struct dw_mipi_dsi_dphy_timing timing;
+};
+
+struct pll_parameter {
+ u32 max_freq;
+ u32 actual_freq;
+ u16 hs_freq_range;
+ u16 pll_n;
+ u16 pll_m;
+ u8 vco_cntrl;
+ u8 cpbias_cntrl;
+ u8 gmp_cntrl;
+ u8 int_cntrl;
+ u8 prop_cntrl;
+};
+
+#define HSTT(_maxfreq, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \
+ { \
+ .maxfreq = _maxfreq, .timing = { \
+ .clk_lp2hs = _c_lp2hs, \
+ .clk_hs2lp = _c_hs2lp, \
+ .data_lp2hs = _d_lp2hs, \
+ .data_hs2lp = _d_hs2lp, \
+ } \
+ }
+
+#define PLL_PARAM(_maxfreq, _vco_ctl, _cpbias_ctl, _gmp_ctl, _int_ctl, \
+ _prop_ctl) \
+ { \
+ .max_freq = _maxfreq, .vco_cntrl = _vco_ctl, \
+ .cpbias_cntrl = _cpbias_ctl, .gmp_cntrl = _gmp_ctl, \
+ .int_cntrl = _int_ctl, .prop_cntrl = _prop_ctl, \
+ }
+
+struct mipi_dsi_priv {
+ u32 lanes;
+ u32 format;
+ unsigned long mode_flags;
+
+ struct pll_parameter pll_param;
+
+ void __iomem *dphy_base;
+
+ struct clk *ivideo_clk;
+ u64 pll_ref_clk;
+
+ struct dw_mipi_dsi_plat_data plat_data;
+ struct reset_control *rst_dsi_phyrstn;
+ void *data;
+};
+
+struct es_mipi_dsi {
+ struct mipi_dsi_priv dsi_priv;
+ struct drm_encoder encoder;
+ struct drm_crtc *crtc;
+};
+
+/* Table A-4 High-Speed Transition Times (High-Speed Entry LP->HS DATA LANE(Considers HS_CLK_LANE_ENTRY)d)*/
+struct hstt hstt_table[] = {
+ HSTT(80, 21, 17, 35, 10), HSTT(90, 23, 17, 39, 10),
+ HSTT(100, 22, 17, 37, 10), HSTT(110, 25, 18, 43, 11),
+ HSTT(120, 26, 20, 46, 11), HSTT(130, 27, 19, 46, 11),
+ HSTT(140, 27, 19, 46, 11), HSTT(150, 28, 20, 47, 12),
+ HSTT(160, 30, 21, 53, 13), HSTT(170, 30, 21, 55, 13),
+ HSTT(180, 31, 21, 53, 13), HSTT(190, 32, 22, 58, 13),
+ HSTT(205, 35, 22, 58, 13), HSTT(220, 37, 26, 63, 15),
+ HSTT(235, 38, 28, 65, 16), HSTT(250, 41, 29, 71, 17),
+ HSTT(275, 43, 29, 74, 18), HSTT(300, 45, 32, 80, 19),
+ HSTT(325, 48, 33, 86, 18), HSTT(350, 51, 35, 91, 20),
+ HSTT(400, 59, 37, 102, 21), HSTT(450, 65, 40, 115, 23),
+ HSTT(500, 71, 41, 126, 24), HSTT(550, 77, 44, 135, 26),
+ HSTT(600, 82, 46, 147, 27), HSTT(650, 87, 48, 156, 28),
+ HSTT(700, 94, 52, 166, 29), HSTT(750, 99, 52, 175, 31),
+ HSTT(800, 105, 55, 187, 32), HSTT(850, 110, 58, 196, 32),
+ HSTT(900, 115, 58, 206, 35), HSTT(950, 120, 62, 213, 36),
+ HSTT(1000, 128, 63, 225, 38), HSTT(1050, 132, 65, 234, 38),
+ HSTT(1100, 138, 67, 243, 39), HSTT(1150, 146, 69, 259, 42),
+ HSTT(1200, 151, 71, 269, 43), HSTT(1250, 153, 74, 273, 45),
+ HSTT(1300, 160, 73, 282, 46), HSTT(1350, 165, 76, 294, 47),
+ HSTT(1400, 172, 78, 304, 49), HSTT(1450, 177, 80, 314, 49),
+ HSTT(1500, 183, 81, 326, 52), HSTT(1550, 191, 84, 339, 52),
+ HSTT(1600, 194, 85, 345, 52), HSTT(1650, 201, 86, 355, 53),
+ HSTT(1700, 208, 88, 368, 53), HSTT(1750, 212, 89, 378, 53),
+ HSTT(1800, 220, 90, 389, 54), HSTT(1850, 223, 92, 401, 54),
+ HSTT(1900, 231, 91, 413, 55), HSTT(1950, 236, 95, 422, 56),
+ HSTT(2000, 243, 97, 432, 56), HSTT(2050, 248, 99, 442, 58),
+ HSTT(2100, 252, 100, 454, 59), HSTT(2150, 259, 102, 460, 61),
+ HSTT(2200, 266, 105, 476, 62), HSTT(2250, 269, 109, 481, 63),
+ HSTT(2300, 272, 109, 490, 65), HSTT(2350, 281, 112, 502, 66),
+ HSTT(2400, 283, 115, 509, 66), HSTT(2450, 282, 115, 510, 67),
+ HSTT(2500, 281, 118, 508, 67),
+};
+
+static struct pll_parameter pll_tbl[] = {
+ PLL_PARAM(55, 0x3f, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(82, 0x37, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(110, 0x2f, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(165, 0x27, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(220, 0x1f, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(330, 0x17, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(440, 0x0f, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(660, 0x07, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(1149, 0x03, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(1152, 0x01, 0x10, 0x01, 0x00, 0x0D),
+ PLL_PARAM(1250, 0x01, 0x10, 0x01, 0x00, 0x0E),
+};
+
+static const struct of_device_id es_mipi_dsi_dt_ids[] = {
+ {
+ .compatible = "eswin,dsi",
+ },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, es_mipi_dsi_dt_ids);
+
+static void dsi_write(struct mipi_dsi_priv *priv, u32 reg, u32 val)
+{
+ writel(val, priv->dphy_base + reg);
+}
+
+static u32 dsi_read(struct mipi_dsi_priv *priv, u32 reg)
+{
+ return readl(priv->dphy_base + reg);
+}
+
+static void es_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct es_mipi_dsi *es_dsi =
+ container_of(encoder, struct es_mipi_dsi, encoder);
+ struct mipi_dsi_priv *dsi_priv = &es_dsi->dsi_priv;
+
+ DRM_INFO("enter, encoder = 0x%px\n", encoder);
+
+ clk_disable_unprepare(dsi_priv->ivideo_clk);
+}
+
+static bool es_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct es_mipi_dsi *es_dsi =
+ container_of(encoder, struct es_mipi_dsi, encoder);
+ struct drm_crtc_state *crtc_state =
+ container_of(mode, struct drm_crtc_state, mode);
+
+ DRM_INFO("enter, bind crtc%d\n", drm_crtc_index(crtc_state->crtc));
+ DRM_INFO("mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+ DRM_INFO("adj_mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj_mode));
+
+ es_dsi->crtc = crtc_state->crtc;
+ return true;
+}
+
+static void es_dsi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ DRM_INFO("mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+ DRM_INFO("adj_mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj_mode));
+}
+
+static void es_dsi_encoder_mode_enable(struct drm_encoder *encoder)
+{
+ int ret;
+ struct es_mipi_dsi *es_dsi =
+ container_of(encoder, struct es_mipi_dsi, encoder);
+ struct mipi_dsi_priv *dsi_priv = &es_dsi->dsi_priv;
+
+ DRM_INFO("enter, encoder = 0x%px\n", encoder);
+
+ ret = clk_prepare_enable(dsi_priv->ivideo_clk);
+ if (ret)
+ DRM_ERROR("enable ivideo clk failed, ret = %d\n", ret);
+}
+
+static const struct drm_encoder_helper_funcs es_dsi_encoder_helper_funcs = {
+ .mode_fixup = es_dsi_encoder_mode_fixup,
+ .mode_set = es_dsi_encoder_mode_set,
+ .enable = es_dsi_encoder_mode_enable,
+ .disable = es_dsi_encoder_disable,
+};
+
+static void es_mipi_dsi_phy_write(struct mipi_dsi_priv *dsi, u8 test_code,
+ u8 test_data)
+{
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current test code. Test data is
+ * programmed internally by rising edge on TESTCLK.
+ */
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1,
+ PHY_TESTEN | PHY_TESTDOUT(0) | PHY_TESTDIN(test_code));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1,
+ PHY_UNTESTEN | PHY_TESTDOUT(0) | PHY_TESTDIN(test_data));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static void es_mipi_dsi_phy_write_m(struct mipi_dsi_priv *dsi, u16 m)
+{
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL1,
+ PHY_TESTEN | PHY_TESTDOUT(0) | PLL_LOOP_DIVIDER_RATIO);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL1,
+ LOOP_DIV_HIGH_SEL(m) | HIGH_PROGRAM_EN);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL1, LOOP_DIV_LOW_SEL(m) | LOW_PROGRAM_EN);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+}
+
+static int es_dsi_phy_init(void *priv_data)
+{
+ struct mipi_dsi_priv *dsi_priv = (struct mipi_dsi_priv *)priv_data;
+ struct pll_parameter *pll_param = &dsi_priv->pll_param;
+
+ dsi_write(dsi_priv, DSI_TO_CNT_CFG, HSTX_TO_CNT(0) | LPRX_TO_CNT(0));
+
+ es_mipi_dsi_phy_write(dsi_priv,
+ PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+ PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+
+ DRM_INFO(
+ "set pll m:0x%x, n:0x%x, vco_cntrl:0x%x ,cpbias:0x%x, gmp:0x%x, int:0x%x, prop:0x%x\n",
+ pll_param->pll_m, pll_param->pll_n, pll_param->vco_cntrl,
+ pll_param->cpbias_cntrl, pll_param->gmp_cntrl,
+ pll_param->int_cntrl, pll_param->prop_cntrl);
+ // 0x17 cfg -> n
+ es_mipi_dsi_phy_write(dsi_priv, PLL_INPUT_DIVIDER_RATIO,
+ INPUT_DIVIDER(pll_param->pll_n));
+
+ es_mipi_dsi_phy_write_m(dsi_priv, pll_param->pll_m);
+
+ // 0x12 cfg
+ es_mipi_dsi_phy_write(dsi_priv, PLL_LPF_AND_CP_CONTROL,
+ LPF_PROGRAM_EN | pll_param->vco_cntrl);
+
+ // 0x1c cfg
+ es_mipi_dsi_phy_write(dsi_priv, 0x1c, pll_param->cpbias_cntrl);
+
+ // 0x13 cfg
+ es_mipi_dsi_phy_write(dsi_priv, 0x13, pll_param->gmp_cntrl << 4);
+
+ // 0x0f cfg
+ es_mipi_dsi_phy_write(dsi_priv, 0x0f, pll_param->int_cntrl);
+ // 0x0f cfg
+ es_mipi_dsi_phy_write(dsi_priv, 0x0e, pll_param->prop_cntrl);
+
+ return 0;
+}
+
+static int dsi_phy_pll_get_params(struct mipi_dsi_priv *dsi_priv, u64 lane_bps)
+{
+ int i;
+ unsigned int min_prediv, max_prediv;
+ unsigned int _prediv0 = 1, _prediv, best_prediv = 0;
+ unsigned long best_freq = 0;
+ unsigned long fvco_min, fvco_max, fin, fout, fvco;
+ unsigned long f_multi, best_multi;
+ unsigned long min_delta = ULONG_MAX;
+ struct pll_parameter *pll_param = &dsi_priv->pll_param;
+
+ fin = dsi_priv->pll_ref_clk;
+ fout = lane_bps / 2;
+
+ /* constraint: 2Mhz <= Fref / N <= 8MHz */
+ min_prediv = DIV_ROUND_UP(fin, 8 * USEC_PER_SEC);
+ max_prediv = fin / (2 * USEC_PER_SEC);
+
+ /* constraint: 320MHz <= Fvco <= 1250Mhz */
+ fvco_min = 320 * USEC_PER_SEC;
+ fvco_max = 1250 * USEC_PER_SEC;
+
+ if (fout * 4 < fvco_min)
+ _prediv0 = 8;
+ else if (fout * 2 < fvco_min)
+ _prediv0 = 4;
+ else if (fout < fvco_min)
+ _prediv0 = 2;
+
+ fvco = fout * _prediv0;
+
+ for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+ u64 tmp;
+ u32 delta;
+
+ /* Fvco = Fref * M / N */
+ tmp = (u64)fvco * _prediv;
+ do_div(tmp, fin);
+ f_multi = tmp;
+ /* M range */
+ if (f_multi < 64 || f_multi > 625)
+ continue;
+
+ tmp = (u64)f_multi * fin;
+ do_div(tmp, _prediv); // fvco
+ if (tmp < fvco_min || tmp > fvco_max)
+ continue;
+
+ delta = abs(fvco - tmp);
+ if (delta < min_delta) {
+ best_prediv = _prediv;
+ best_multi = f_multi; // M
+ min_delta = delta;
+ best_freq = tmp / _prediv0; // fout
+ }
+ }
+
+ if (best_freq) {
+ pll_param->pll_n = best_prediv - 1;
+ pll_param->pll_m = best_multi - 2;
+ pll_param->actual_freq = best_freq;
+ } else {
+ DRM_ERROR("Can not find best_freq for DPHY\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_tbl); i++) {
+ if (pll_param->actual_freq <=
+ pll_tbl[i].max_freq * USEC_PER_SEC) {
+ pll_param->max_freq =
+ pll_tbl[i].max_freq * USEC_PER_SEC;
+ /* cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4] */
+ pll_param->vco_cntrl = pll_tbl[i].vco_cntrl;
+ pll_param->cpbias_cntrl = pll_tbl[i].cpbias_cntrl;
+ pll_param->gmp_cntrl = pll_tbl[i].gmp_cntrl;
+ pll_param->int_cntrl = pll_tbl[i].int_cntrl;
+ pll_param->prop_cntrl = pll_tbl[i].prop_cntrl;
+ break;
+ }
+ }
+
+ DRM_INFO(
+ "max_freq = %d, actual_freq = %d, pll_n = %d, pll_m = %d, i=%d\n",
+ pll_param->max_freq, pll_param->actual_freq, pll_param->pll_n,
+ pll_param->pll_m, i);
+ return 0;
+}
+
+static int es_dsi_get_lane_mbps(void *priv_data,
+ const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format,
+ unsigned int *lane_mbps)
+{
+ int ret, bpp;
+ u64 tmp;
+ struct mipi_dsi_priv *dsi_priv = (struct mipi_dsi_priv *)priv_data;
+
+ DRM_INFO("mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+ DRM_INFO("lanes: %d, mode_flags: 0x%lx, format: %d\n", lanes,
+ mode_flags, format);
+
+ dsi_priv->format = format;
+ bpp = mipi_dsi_pixel_format_to_bpp(dsi_priv->format);
+ if (bpp < 0) {
+ DRM_DEV_ERROR(NULL, "failed to get bpp for pixel format %d\n",
+ dsi_priv->format);
+ return bpp;
+ }
+
+ if (!lanes)
+ lanes = dsi_priv->plat_data.max_data_lanes;
+
+ dsi_priv->mode_flags = mode_flags;
+ dsi_priv->lanes = lanes;
+
+ tmp = (u64)mode->clock * 1000;
+
+ tmp = tmp * bpp / lanes;
+
+ ret = dsi_phy_pll_get_params(dsi_priv, tmp);
+ if (ret)
+ return ret;
+
+ *lane_mbps =
+ DIV_ROUND_UP(dsi_priv->pll_param.actual_freq * 2, USEC_PER_SEC);
+
+ DRM_INFO("bpp = %d, lane_mbps = %d\n", bpp, *lane_mbps);
+
+ return 0;
+}
+
+static int es_dsi_get_timing(void *priv_data, unsigned int lane_mbps,
+ struct dw_mipi_dsi_dphy_timing *timing)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hstt_table); i++)
+ if (lane_mbps < hstt_table[i].maxfreq)
+ break;
+
+ if (i == ARRAY_SIZE(hstt_table))
+ i--;
+
+ *timing = hstt_table[i].timing;
+
+ DRM_INFO(
+ "lane_mbps: %d, c_lp2hs: %d, c_hs2lp: %d, d_lp2hs: %d, d_hs2lp: %d\n",
+ lane_mbps, timing->clk_lp2hs, timing->clk_hs2lp,
+ timing->data_lp2hs, timing->data_lp2hs);
+
+ return 0;
+}
+
+struct dw_mipi_dsi_phy_ops dphy_ops = {
+ .init = es_dsi_phy_init,
+ .get_lane_mbps = es_dsi_get_lane_mbps,
+ .get_timing = es_dsi_get_timing,
+};
+
+static int es_mipi_dsi_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int ret = 0;
+ unsigned int max_data_lanes;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm_dev = (struct drm_device *)data;
+ struct es_mipi_dsi *es_dsi;
+ struct mipi_dsi_priv *dsi_priv;
+ struct drm_encoder *encoder;
+ struct resource *res;
+
+ es_dsi = devm_kzalloc(&pdev->dev, sizeof(*es_dsi), GFP_KERNEL);
+ if (!es_dsi) {
+ ret = -ENODEV;
+ goto exit0;
+ }
+
+ if (!pdev->dev.of_node) {
+ DRM_ERROR("of_node is null, dev: %px\n", dev);
+ ret = -ENODEV;
+ goto exit0;
+ }
+
+ dsi_priv = &es_dsi->dsi_priv;
+
+ ret = of_property_read_u32(pdev->dev.of_node, "max-lanes",
+ &max_data_lanes);
+ if (ret) {
+ DRM_WARN("max-lanes not defined, default max-lanes is 4\n");
+ max_data_lanes = MAX_LANE_COUNT;
+ }
+ dsi_priv->plat_data.max_data_lanes = max_data_lanes;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dsi_priv->dphy_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dsi_priv->dphy_base)) {
+ DRM_ERROR("get mm_sys register base failed, of_node: %pOF\n",
+ dev->of_node);
+ ret = PTR_ERR(dsi_priv->dphy_base);
+ goto exit0;
+ }
+
+ dsi_priv->plat_data.base = dsi_priv->dphy_base;
+
+ dsi_priv->plat_data.phy_ops = &dphy_ops;
+ dsi_priv->plat_data.priv_data = dsi_priv;
+ dsi_priv->pll_ref_clk = PLL_INPUT_REF_CLK;
+
+ dsi_priv->ivideo_clk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dsi_priv->ivideo_clk)) {
+ DRM_ERROR("get ivideo clk failed, of_node: %pOF\n",
+ dev->of_node);
+ ret = PTR_ERR(dsi_priv->ivideo_clk);
+ goto exit0;
+ }
+
+ /* dsi phyctl reset */
+ dsi_priv->rst_dsi_phyrstn =
+ devm_reset_control_get_optional(dev, "phyrstn");
+ if (IS_ERR_OR_NULL(dsi_priv->rst_dsi_phyrstn)) {
+ DRM_ERROR("Failed to get dsi phyrstn reset handle\n");
+ goto exit0;
+ }
+ if (dsi_priv->rst_dsi_phyrstn) {
+ ret = reset_control_reset(dsi_priv->rst_dsi_phyrstn);
+ WARN_ON(0 != ret);
+ }
+
+ encoder = &es_dsi->encoder;
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+ if (encoder->possible_crtcs == 0) {
+ DRM_ERROR("Failed to find possible_crtcs:0\n");
+ }
+
+ ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+ if (ret) {
+ DRM_ERROR("Failed to initialize encoder with drm\n");
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &es_dsi_encoder_helper_funcs);
+
+ platform_set_drvdata(pdev, es_dsi);
+
+ dsi_priv->data = dw_mipi_dsi_probe(pdev, &dsi_priv->plat_data);
+ if (IS_ERR(dsi_priv->data)) {
+ DRM_ERROR("probe dw-mipi dsi failed\n");
+ ret = -ENODEV;
+ goto exit2;
+ }
+
+ ret = dw_mipi_dsi_bind((struct dw_mipi_dsi *)dsi_priv->data, encoder);
+ if (ret)
+ goto exit3;
+
+ DRM_INFO("mipi dsi bind done\n");
+
+ return 0;
+
+exit3:
+ dw_mipi_dsi_remove((struct dw_mipi_dsi *)dsi_priv->data);
+exit2:
+ drm_encoder_cleanup(encoder);
+exit0:
+ DRM_INFO("mipi dsi bind failed, ret = %d\n", ret);
+ return ret;
+}
+
+static void es_mipi_dsi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct es_mipi_dsi *es_dsi =
+ (struct es_mipi_dsi *)platform_get_drvdata(pdev);
+ struct mipi_dsi_priv *dsi_priv = &es_dsi->dsi_priv;
+
+ DRM_INFO("mipi dsi unbind\n");
+
+ dw_mipi_dsi_unbind((struct dw_mipi_dsi *)dsi_priv->data);
+ dw_mipi_dsi_remove((struct dw_mipi_dsi *)dsi_priv->data);
+ drm_encoder_cleanup(&es_dsi->encoder);
+}
+
+static const struct component_ops es_mipi_dsi_ops = {
+ .bind = es_mipi_dsi_bind,
+ .unbind = es_mipi_dsi_unbind,
+};
+
+static int es_mipi_dsi_probe(struct platform_device *pdev)
+{
+ mipi_dsi_driver_register(&es_panel_driver);
+
+ return component_add(&pdev->dev, &es_mipi_dsi_ops);
+}
+
+static int es_mipi_dsi_remove(struct platform_device *pdev)
+{
+ DRM_INFO("mipi dsi remove\n");
+ mipi_dsi_driver_unregister(&es_panel_driver);
+ component_del(&pdev->dev, &es_mipi_dsi_ops);
+
+ return 0;
+}
+
+struct platform_driver es_mipi_dsi_driver = {
+ .probe = es_mipi_dsi_probe,
+ .remove = es_mipi_dsi_remove,
+ .driver = {
+ .of_match_table = es_mipi_dsi_dt_ids,
+ .name = "mipi-dsi-drv",
+ },
+};
+
+MODULE_AUTHOR("lilijun@eswin.com");
+MODULE_DESCRIPTION("Eswin dw dsi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_mipi_dsi.h b/drivers/gpu/drm/eswin/es_mipi_dsi.h
new file mode 100644
index 000000000000..4a757e527736
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_mipi_dsi.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_MIPI_DSI_H__
+#define __ES_MIPI_DSI_H__
+
+extern struct platform_driver es_mipi_dsi_driver;
+
+#endif // __ES_MIPI_DSI_H__
\ No newline at end of file
diff --git a/drivers/gpu/drm/eswin/es_panel.c b/drivers/gpu/drm/eswin/es_panel.c
new file mode 100644
index 000000000000..7c2bd90393e2
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_panel.c
@@ -0,0 +1,943 @@
+#include "es_panel.h"
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_modes.h>
+#include <linux/backlight.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/media-bus-format.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/gpio/consumer.h>
+#include <drm/drm_print.h>
+#include <linux/version.h>
+
+#define ES_PANEL_MAJOR 0
+#define ES_PANEL_NR_DEVS 1
+#define ES_PANEL_NAME "es_panel"
+
+static int panel_major = ES_PANEL_MAJOR;
+static int panel_minor = 0;
+static int panel_nr_devs = ES_PANEL_NR_DEVS;
+static struct device *pr_dev = NULL;
+
+typedef struct user_cmd_buffer_s {
+ struct list_head head;
+ u32 sleepUs;
+ int buf_size;
+ u8 *buf;
+} user_cmd_buffer_t;
+
+struct es_panel {
+ struct device *dev;
+ struct drm_panel panel;
+ struct backlight_device *backlight;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *bias_pos, *bias_neg;
+ bool prepared;
+ bool enabled;
+ int error;
+
+ // chr dev:
+ struct cdev cdev;
+ dev_t devt;
+ struct class *es_panel_class;
+
+ // user data:
+ MIPI_MODE_INFO_S mode_info;
+ struct drm_display_mode user_mode;
+ bool user_mode_inited;
+
+ struct list_head init_cmd_list;
+ int init_cmd_writted;
+ user_cmd_buffer_t enable_cmd_buf;
+ user_cmd_buffer_t disable_cmd_buf;
+
+ struct gpio_desc *gpio_backlight0;
+ struct gpio_desc *gpio_reset;
+};
+
+static void es_panel_dcs_write(struct es_panel *ctx, const void *data,
+ size_t len);
+
+#define es_panel_dcs_write_seq_static(ctx, seq...) \
+ ({ \
+ static const u8 d[] = { seq }; \
+ es_panel_dcs_write(ctx, d, ARRAY_SIZE(d)); \
+ udelay(10); \
+ })
+
+#define es_panel_check_retval(x) \
+ do { \
+ if ((x)) \
+ return -EIO; \
+ } while (0)
+
+// #ifdef CONFIG_1080P_4LANES_PANEL
+#define HFP (60)
+#define HSA (60)
+#define HBP (60)
+#define VFP (8)
+#define VSA (8)
+#define VBP (8)
+#define VAC (1920)
+#define HAC (1080)
+#define LANES 4
+#define PIX_CLK 148500
+// #elif CONFIG_720P_3LANES_PANEL
+// #define HFP (30)
+// #define HSA (30)
+// #define HBP (30)
+// #define VFP (30)
+// #define VSA (30)
+// #define VBP (30)
+// #define VAC (1280)
+// #define HAC (720)
+// #define LANES 3
+// #define PIX_CLK 59400
+// #else
+// #define HFP (60)
+// #define HSA (60)
+// #define HBP (60)
+// #define VFP (8)
+// #define VSA (8)
+// #define VBP (8)
+// #define VAC (1920)
+// #define HAC (1080)
+// #define LANES 4
+// #define PIX_CLK 148500
+// #endif
+
+static struct drm_display_mode default_mode = {
+ .clock = PIX_CLK,
+ .hdisplay = HAC,
+ .hsync_start = HAC + HFP,
+ .hsync_end = HAC + HFP + HSA,
+ .htotal = HAC + HFP + HSA + HBP,
+ .vdisplay = VAC,
+ .vsync_start = VAC + VFP,
+ .vsync_end = VAC + VFP + VSA,
+ .vtotal = VAC + VFP + VSA + VBP,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+void GP_COMMAD_PA(struct es_panel *ctx, u8 a)
+{
+ return;
+ // u8 b[3] = {0};
+ // b[0] = 0xBC;
+ // b[1] = a;
+ // b[2] = a >> 8;
+ // es_panel_dcs_write(ctx, b, 3);
+ // es_panel_dcs_write_seq_static(ctx, 0xBf);
+}
+
+static void free_cmd_buf(user_cmd_buffer_t *pcmd_buf)
+{
+ if (pcmd_buf->buf) {
+ pcmd_buf->buf_size = 0;
+ vfree(pcmd_buf->buf);
+ pcmd_buf->buf = NULL;
+ }
+ return;
+}
+
+void free_cmd_list(struct list_head *list)
+{
+ if (!list_empty(list)) {
+ user_cmd_buffer_t *pos = NULL, *n = NULL;
+ list_for_each_entry_safe(pos, n, list, head) {
+ if (pos) {
+ list_del_init(&pos->head);
+ if (pos->buf) {
+ vfree(pos->buf);
+ pos->buf = NULL;
+ }
+ vfree(pos);
+ pos = NULL;
+ }
+ }
+ }
+}
+
+static int parser_and_creat_cmd_buf(MIPI_CMD_S *cmd, user_cmd_buffer_t *cmd_buf)
+{
+ if (!cmd) {
+ dev_err(pr_dev, "cmd invalid\n");
+ return -EPERM;
+ }
+ if (!cmd_buf) {
+ dev_err(pr_dev, "cmd_buf invalid\n");
+ return -EPERM;
+ }
+
+ if (cmd_buf->buf) {
+ vfree(cmd_buf->buf);
+ cmd_buf->buf = NULL;
+ }
+
+ if (cmd->dataType == 0x13) {
+ cmd_buf->buf_size = 1;
+ cmd_buf->buf = vmalloc(cmd_buf->buf_size);
+ cmd_buf->buf[0] = cmd->cmdSize;
+ } else if (cmd->dataType == 0x23) {
+ cmd_buf->buf_size = 2;
+ cmd_buf->buf = vmalloc(cmd_buf->buf_size);
+ cmd_buf->buf[0] = cmd->cmdSize >> 8;
+ cmd_buf->buf[1] = cmd->cmdSize & 0x00ff;
+ } else if (cmd->dataType == 0x29) {
+ cmd_buf->buf_size = cmd->cmdSize;
+ cmd_buf->buf = vmalloc(cmd_buf->buf_size);
+ if (copy_from_user(cmd_buf->buf, (u8 __user *)cmd->pCmd,
+ cmd_buf->buf_size)) {
+ dev_err(pr_dev, "cmd buffer copy_from_user failed\n");
+ return -ENOMEM;
+ }
+ }
+ cmd_buf->sleepUs = cmd->sleepUs;
+ dev_dbg(pr_dev, "user set cmd size:%d, sleep:%dus,cmd:0x%x \n",
+ cmd_buf->buf_size, cmd_buf->sleepUs, cmd_buf->buf[0]);
+ return 0;
+}
+
+int es_panel_fops_open(struct inode *inode, struct file *filp)
+{
+ struct es_panel *ctx;
+ ctx = container_of(inode->i_cdev, struct es_panel, cdev);
+ filp->private_data = ctx;
+ return 0;
+}
+
+int es_panel_fops_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static long es_panel_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct es_panel *ctx;
+ struct mipi_dsi_device *dsi;
+ MIPI_CMD_S ucmd;
+ int ret = 0;
+
+ ctx = (struct es_panel *)file->private_data;
+ dsi = to_mipi_dsi_device(ctx->dev);
+
+ switch (cmd) {
+ case IOC_ES_MIPI_TX_SET_MODE:
+ es_panel_check_retval(copy_from_user(
+ &ctx->mode_info, (MIPI_MODE_INFO_S __user *)arg,
+ sizeof(MIPI_MODE_INFO_S)));
+ dev_dbg(pr_dev,
+ "cmd: IOC_ES_MIPI_TX_SET_MODE, devId:%d, hdisplay:%u, hsyncStart:%u, hsyncEnd:%u, htotal:%u, hskew:%u\n"
+ "vdisplay:%u vsyncStart:%u vsyncEnd:%u vtotal:%u vscan:%u vrefresh:%d flags:%d type:%d "
+ "outputFormat:%d outputMode:%d lanes:%d\n",
+ ctx->mode_info.devId, ctx->mode_info.hdisplay,
+ ctx->mode_info.hsyncStart, ctx->mode_info.hsyncEnd,
+ ctx->mode_info.htotal, ctx->mode_info.hskew,
+ ctx->mode_info.vdisplay, ctx->mode_info.vsyncStart,
+ ctx->mode_info.vsyncEnd, ctx->mode_info.vtotal,
+ ctx->mode_info.vscan, ctx->mode_info.vrefresh,
+ ctx->mode_info.flags, ctx->mode_info.type,
+ ctx->mode_info.outputFormat, ctx->mode_info.outputMode,
+ ctx->mode_info.lanes);
+
+ memset(&ctx->user_mode, 0, sizeof(struct drm_display_mode));
+ ctx->user_mode.clock = 12500;
+ ctx->user_mode.hdisplay = ctx->mode_info.hdisplay;
+ ctx->user_mode.hsync_start = ctx->mode_info.hsyncStart;
+ ctx->user_mode.hsync_end = ctx->mode_info.hsyncEnd;
+ ctx->user_mode.htotal = ctx->mode_info.htotal;
+ ctx->user_mode.vdisplay = ctx->mode_info.vdisplay;
+ ctx->user_mode.vsync_start = ctx->mode_info.vsyncStart;
+ ctx->user_mode.vsync_end = ctx->mode_info.vsyncEnd;
+ ctx->user_mode.vtotal = ctx->mode_info.vtotal;
+ ctx->user_mode.width_mm = 36;
+ ctx->user_mode.height_mm = 49;
+ ctx->user_mode.flags = DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_NHSYNC;
+ ctx->user_mode_inited = true;
+
+ dsi->lanes = ctx->mode_info.lanes;
+ if (ctx->mode_info.outputFormat == MIPI_OUT_FORMAT_RGB_24_BIT) {
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ } else {
+ dev_err(pr_dev, "panel not support fmt:%d now",
+ ctx->mode_info.outputFormat);
+ }
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
+
+ break;
+ case IOC_ES_MIPI_TX_SET_INIT_CMD:
+ user_cmd_buffer_t *cmd_buf = NULL;
+
+ if (ctx->init_cmd_writted) {
+ dev_info(pr_dev,
+ "init_cmd_writted : clean init cmd list\n");
+ free_cmd_list(&ctx->init_cmd_list);
+ INIT_LIST_HEAD(&ctx->init_cmd_list);
+ ctx->init_cmd_writted = 0;
+ }
+ cmd_buf = vmalloc(sizeof(user_cmd_buffer_t));
+ if (!cmd_buf) {
+ dev_err(pr_dev, "cmd buffer alloc failed\n");
+ return -ENOMEM;
+ }
+ memset(cmd_buf, 0, sizeof(user_cmd_buffer_t));
+
+ es_panel_check_retval(copy_from_user(
+ &ucmd, (MIPI_CMD_S __user *)arg, sizeof(MIPI_CMD_S)));
+ ret = parser_and_creat_cmd_buf(&ucmd, cmd_buf);
+ if (ret) {
+ break;
+ }
+ if (!cmd_buf) {
+ dev_err(pr_dev, "cmd_buf creat failed!!!!");
+ break;
+ }
+ list_add_tail(&cmd_buf->head, &ctx->init_cmd_list);
+
+ break;
+ case IOC_ES_MIPI_TX_ENABLE:
+ user_cmd_buffer_t *enable_cmd_buf = &ctx->enable_cmd_buf;
+ dev_dbg(pr_dev, "IOC_ES_MIPI_TX_ENABLE In");
+ es_panel_check_retval(copy_from_user(
+ &ucmd, (MIPI_CMD_S __user *)arg, sizeof(MIPI_CMD_S)));
+ ret = parser_and_creat_cmd_buf(&ucmd, enable_cmd_buf);
+ break;
+ case IOC_ES_MIPI_TX_DISABLE:
+ user_cmd_buffer_t *disable_cmd_buf = &ctx->disable_cmd_buf;
+ dev_dbg(pr_dev, "IOC_ES_MIPI_TX_DISABLE In");
+ es_panel_check_retval(copy_from_user(
+ &ucmd, (MIPI_CMD_S __user *)arg, sizeof(MIPI_CMD_S)));
+ ret = parser_and_creat_cmd_buf(&ucmd, disable_cmd_buf);
+ break;
+ default:
+ dev_err(pr_dev, "unsupported command %d", cmd);
+ break;
+ }
+ return ret;
+}
+
+struct file_operations es_panel_fops = {
+ .owner = THIS_MODULE,
+ .open = es_panel_fops_open,
+ .release = es_panel_fops_release,
+ .unlocked_ioctl = es_panel_fops_ioctl,
+};
+
+int es_panel_chrdev_create(struct es_panel *ctx)
+{
+ int ret = 0;
+ dev_dbg(pr_dev, "---BEGIN es_panel_chrdev_create---\n");
+
+ ret = alloc_chrdev_region(&ctx->devt, panel_minor, panel_nr_devs,
+ ES_PANEL_NAME);
+ if (ret < 0) {
+ dev_err(pr_dev,
+ "es-panel-chrdev: can't allocate major number\n");
+ goto fail;
+ }
+ panel_major = MAJOR(ctx->devt);
+
+ cdev_init(&ctx->cdev, &es_panel_fops);
+ ctx->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&ctx->cdev, ctx->devt, 1);
+ if (ret) {
+ dev_err(pr_dev, "failed to add cdev\n");
+ goto unregister_chrdev;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
+ ctx->es_panel_class = class_create(ES_PANEL_NAME);
+#else
+ ctx->es_panel_class = class_create(THIS_MODULE, ES_PANEL_NAME);
+#endif
+ if (IS_ERR(ctx->es_panel_class)) {
+ dev_err(pr_dev, "class_create error\n");
+ ret = PTR_ERR(ctx->es_panel_class);
+ goto del_cdev;
+ }
+
+ if (device_create(ctx->es_panel_class, NULL, ctx->devt, ctx,
+ ES_PANEL_NAME) == NULL) {
+ dev_err(pr_dev, "device_create error\n");
+ ret = -EINVAL;
+ goto destroy_class;
+ }
+
+ dev_dbg(pr_dev, "---END es_panel_chrdev_create---\n");
+ return 0;
+
+destroy_class:
+ class_destroy(ctx->es_panel_class);
+del_cdev:
+ cdev_del(&ctx->cdev);
+unregister_chrdev:
+ unregister_chrdev_region(ctx->devt, panel_nr_devs);
+fail:
+ return ret;
+}
+
+void es_panel_chrdev_destroy(struct es_panel *ctx)
+{
+ device_destroy(ctx->es_panel_class, MKDEV(panel_major, panel_minor));
+ class_destroy(ctx->es_panel_class);
+
+ cdev_del(&ctx->cdev);
+
+ unregister_chrdev_region(ctx->devt, panel_nr_devs);
+ dev_dbg(pr_dev, "es_panel_chrdev_destroy done\n");
+}
+
+static inline struct es_panel *panel_to_es_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct es_panel, panel);
+}
+
+static void es_panel_dcs_write(struct es_panel *ctx, const void *data,
+ size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ ssize_t ret;
+ char *addr;
+
+ if (ctx->error < 0)
+ return;
+
+ addr = (char *)data;
+ if ((int)*addr < 0xB0)
+ ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
+ else
+ ret = mipi_dsi_generic_write(dsi, data, len);
+ if (ret < 0) {
+ dev_err(ctx->dev, "error %zd writing seq: %ph\n", ret, data);
+ ctx->error = ret;
+ }
+ dev_dbg(ctx->dev, "write cmd size:%ld, data0[0x%x] ret:%d\n", len,
+ addr[0], ctx->error);
+}
+
+#ifdef PANEL_SUPPORT_READBACK
+static int es_panel_dcs_read(struct es_panel *ctx, u8 cmd, void *data,
+ size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ ssize_t ret;
+
+ if (ctx->error < 0)
+ return 0;
+
+ ret = mipi_dsi_dcs_read(dsi, cmd, data, len);
+ if (ret < 0) {
+ dev_err(ctx->dev, "error %d reading dcs seq:(%#x)\n", ret, cmd);
+ ctx->error = ret;
+ }
+
+ return ret;
+}
+
+static void es_panel_panel_get_data(struct es_panel *ctx)
+{
+ u8 buffer[3] = { 0 };
+ static int ret;
+
+ if (ret == 0) {
+ ret = es_panel_dcs_read(ctx, 0x0A, buffer, 1);
+ dev_info(ctx->dev, "return %d data(0x%08x) to dsi engine\n",
+ ret, buffer[0] | (buffer[1] << 8));
+ }
+}
+#endif
+
+void es_panel_panel_init(struct es_panel *ctx)
+{
+ user_cmd_buffer_t *pos = NULL;
+ list_for_each_entry(pos, &ctx->init_cmd_list, head) {
+ if (pos) {
+ es_panel_dcs_write(ctx, pos->buf, pos->buf_size);
+ if (pos->sleepUs) {
+ msleep(pos->sleepUs / 1000);
+ }
+ }
+ }
+ ctx->init_cmd_writted = 1;
+}
+
+void es_panel_3_lanes_panel_init(struct es_panel *ctx)
+{
+ dev_info(ctx->dev, "init 3lanes panel\n");
+ GP_COMMAD_PA(ctx, 0x04);
+ es_panel_dcs_write_seq_static(ctx, 0xB9, 0xF1, 0x12, 0x83);
+ GP_COMMAD_PA(ctx, 0x1C);
+ es_panel_dcs_write_seq_static(ctx, 0xBA, 0x32, 0x81, 0x05, 0xF9, 0x0E,
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x25, 0x00, 0x91, 0x0A,
+ 0x00, 0x00, 0x02, 0x4F, 0xD1, 0x00, 0x00,
+ 0x37);
+ GP_COMMAD_PA(ctx, 0x05);
+ es_panel_dcs_write_seq_static(ctx, 0xB8, 0x26, 0x22, 0x20, 0x00);
+ GP_COMMAD_PA(ctx, 0x04);
+ es_panel_dcs_write_seq_static(ctx, 0xBF, 0x02, 0x11, 0x00);
+ GP_COMMAD_PA(ctx, 0x0B);
+ es_panel_dcs_write_seq_static(ctx, 0xB3, 0x0C, 0x10, 0x0A, 0x50, 0x03,
+ 0xFF, 0x00, 0x00, 0x00, 0x00);
+ GP_COMMAD_PA(ctx, 0x0A);
+ es_panel_dcs_write_seq_static(ctx, 0xB3, 0x0C, 0x10, 0x0A, 0x50, 0x03,
+ 0xFF, 0x00, 0x00, 0x00, 0x00);
+ GP_COMMAD_PA(ctx, 0x02);
+ es_panel_dcs_write_seq_static(ctx, 0xBC, 0x46);
+ GP_COMMAD_PA(ctx, 0x02);
+ es_panel_dcs_write_seq_static(ctx, 0xCC, 0x0B);
+ GP_COMMAD_PA(ctx, 0x02);
+ es_panel_dcs_write_seq_static(ctx, 0xB4, 0x80);
+ GP_COMMAD_PA(ctx, 0x04);
+ es_panel_dcs_write_seq_static(ctx, 0xB2, 0xC8, 0x02, 0x30);
+ GP_COMMAD_PA(ctx, 0x0F);
+ es_panel_dcs_write_seq_static(ctx, 0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x03,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80,
+ 0xC0, 0x10);
+ GP_COMMAD_PA(ctx, 0x0D);
+ es_panel_dcs_write_seq_static(ctx, 0xC1, 0x25, 0x00, 0x1E, 0x1E, 0x77,
+ 0xF1, 0xFF, 0xFF, 0xCC, 0xCC, 0x77, 0x77);
+ GP_COMMAD_PA(ctx, 0x03);
+ es_panel_dcs_write_seq_static(ctx, 0xB5, 0x0A, 0x0A);
+ GP_COMMAD_PA(ctx, 0x03);
+ es_panel_dcs_write_seq_static(ctx, 0xB6, 0x50, 0x50);
+ GP_COMMAD_PA(ctx, 0x40);
+ es_panel_dcs_write_seq_static(
+ ctx, 0xE9, 0xC4, 0x10, 0x0F, 0x00, 0x00, 0xB2, 0xB8, 0x12, 0x31,
+ 0x23, 0x48, 0x8B, 0xB2, 0xB8, 0x47, 0x20, 0x00, 0x00, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02,
+ 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF8,
+ 0x13, 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00);
+ GP_COMMAD_PA(ctx, 0x3E);
+ es_panel_dcs_write_seq_static(ctx, 0xEA, 0x00, 0x1A, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x75, 0x31, 0x31, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x8F, 0x64, 0x20,
+ 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x8F, 0x23, 0x10, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x02, 0xA0, 0x00, 0x00, 0x00, 0x00);
+ GP_COMMAD_PA(ctx, 0x23);
+ es_panel_dcs_write_seq_static(ctx, 0xE0, 0x01, 0x11, 0x16, 0x28, 0x30,
+ 0x38, 0x45, 0x39, 0x07, 0x0B, 0x0D, 0x12,
+ 0x14, 0x12, 0x14, 0x11, 0x19, 0x01, 0x11,
+ 0x16, 0x28, 0x30, 0x38, 0x45, 0x39, 0x07,
+ 0x0B, 0x0D, 0x12, 0x14, 0x12, 0x14, 0x11,
+ 0x19);
+
+ es_panel_dcs_write_seq_static(ctx, 0xC7, 0xE7, 0x00);
+ // es_panel_dcs_write_seq_static(ctx,0xC8, 0x10, 0x40);
+ es_panel_dcs_write_seq_static(ctx, 0x51,
+ 0xFF); // brightness [0x00-> 0xFF]
+ es_panel_dcs_write_seq_static(ctx, 0x53, (1 << 2) | (1 << 5));
+ msleep(20);
+}
+
+void es_panel_4_lanes_panel_init(struct es_panel *ctx)
+{
+ dev_info(ctx->dev, "init 4lanes panel\n");
+ es_panel_dcs_write_seq_static(ctx, 0xB0, 0x98, 0x85, 0x0A);
+ es_panel_dcs_write_seq_static(ctx, 0xC1, 0x00, 0x00,
+ 0x00); // 01反扫 00正扫
+ es_panel_dcs_write_seq_static(ctx, 0xC4, 0x70, 0x19, 0x23, 0x00, 0x0F,
+ 0x0F, 0x00);
+ es_panel_dcs_write_seq_static(
+ ctx, 0xD0, 0x33, 0x05, 0x21, 0xE7, 0x62, 0x00, 0x88, 0xA0, 0xA0,
+ 0x03, 0x02, 0x80, 0xD0, 0x1B,
+ 0x10); // VGH dual mode, VGL single mode, VGH=12V, VGL=-12V
+ es_panel_dcs_write_seq_static(ctx, 0xD2, 0x13, 0x13, 0xEA, 0x22);
+ es_panel_dcs_write_seq_static(ctx, 0xD1, 0x09, 0x09,
+ 0xc2); ////4003 & 4003B EN
+ es_panel_dcs_write_seq_static(ctx, 0xD3, 0x44, 0x33, 0x05, 0x03, 0x4A,
+ 0x4A, 0x33, 0x17, 0x22, 0x43,
+ 0x6E); // set GVDDP=4.1V, GVDDN=-4.1V,
+ // VGHO=12V, VGLO=-11V
+ es_panel_dcs_write_seq_static(ctx, 0xD5, 0x8B, 0x00, 0x00, 0x00, 0x01,
+ 0x7D, 0x01, 0x7D, 0x01, 0x7D, 0x00, 0x00,
+ 0x00, 0x00); // set VCOM
+ es_panel_dcs_write_seq_static(ctx, 0xD6, 0x00, 0x00, 0x08, 0x17, 0x23,
+ 0x65, 0x77, 0x44, 0x87, 0x00, 0x00,
+ 0x09); // P12_D[3] for sleep in
+ // current reduce setting
+ es_panel_dcs_write_seq_static(ctx, 0xEC, 0x76, 0x1E, 0x32, 0x00, 0x46,
+ 0x00, 0x00);
+ es_panel_dcs_write_seq_static(
+ ctx, 0xC7, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x2D, 0x00, 0x43, 0x00,
+ 0x58, 0x00, 0x6C, 0x00, 0x75, 0x00, 0x8C, 0x00, 0x9A, 0x00,
+ 0xCA, 0x00, 0xF1, 0x01, 0x2F, 0x01, 0x5F, 0x01, 0xAE, 0x01,
+ 0xEC, 0x01, 0xEE, 0x02, 0x25, 0x02, 0x62, 0x02, 0x8A, 0x02,
+ 0xC4, 0x02, 0xEA, 0x03, 0x1F, 0x03, 0x33, 0x03, 0x3E, 0x03,
+ 0x59, 0x03, 0x70, 0x03, 0x88, 0x03, 0xB4, 0x03, 0xC8, 0x03,
+ 0xE8, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x2D, 0x00, 0x43, 0x00,
+ 0x58, 0x00, 0x6C, 0x00, 0x75, 0x00, 0x8C, 0x00, 0x9A, 0x00,
+ 0xCA, 0x00, 0xF1, 0x01, 0x2F, 0x01, 0x5F, 0x01, 0xAE, 0x01,
+ 0xEC, 0x01, 0xEE, 0x02, 0x25, 0x02, 0x62, 0x02, 0x8A, 0x02,
+ 0xC4, 0x02, 0xEA, 0x03, 0x1F, 0x03, 0x33, 0x03, 0x3E, 0x03,
+ 0x59, 0x03, 0x70, 0x03, 0x88, 0x03, 0xB4, 0x03, 0xC8, 0x03,
+ 0xE8, 0x01, 0x01);
+ es_panel_dcs_write_seq_static(
+ ctx, 0xE5, 0x6D, 0x92, 0x80, 0x34, 0x76, 0x12, 0x12, 0x00, 0x00,
+ 0x36, 0x36, 0x24, 0x24, 0x26, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+ 0xF6, 0x6D, 0x9B, 0x89, 0x34, 0x76, 0x1B, 0x1B, 0x09, 0x09,
+ 0x3F, 0x3F, 0x2D, 0x2D, 0x26, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+ 0xF6, 0x04, 0x40, 0x90, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x44, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x00, 0x07);
+ es_panel_dcs_write_seq_static(
+ ctx, 0xEA, 0x70, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x89,
+ 0x8a, 0x05, 0x05, 0x22, 0x0a, 0x0a, 0x0b, 0x00, 0x08, 0x00,
+ 0x30, 0x01, 0x09, 0x00, 0x40, 0x80, 0xc0, 0x00, 0x00, 0x01,
+ 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xDD, 0x22, 0x22,
+ 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xCC, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33);
+ es_panel_dcs_write_seq_static(ctx, 0xEE, 0x22, 0x10, 0x02, 0x02, 0x0F,
+ 0x40, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0xB9, 0x77, 0x00, 0x55, 0x05,
+ 0x00); // 4003
+ es_panel_dcs_write_seq_static(ctx, 0xEB, 0xa3, 0xcf, 0x73, 0x18, 0x54,
+ 0x55, 0x55, 0x55, 0x55, 0x00, 0x55, 0x55,
+ 0x55, 0x55, 0x55, 0x25, 0x0D, 0x0F, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55,
+ 0x55, 0x32, 0x3E, 0x55, 0x43, 0x55);
+
+ es_panel_dcs_write_seq_static(ctx, 0x51, 0x0F,
+ 0xFF); // brightness [0x00-> 0xFFF]
+ es_panel_dcs_write_seq_static(ctx, 0x53, (1 << 2) | (1 << 5));
+}
+
+static int es_panel_disable(struct drm_panel *panel)
+{
+ struct es_panel *ctx = panel_to_es_panel(panel);
+ int ret = 0;
+ user_cmd_buffer_t *cmd_buf = &ctx->disable_cmd_buf;
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(panel->dev);
+
+ dev_dbg(pr_dev, "%s:%d size:%x ctx->enabled:%d\n", __func__, __LINE__,
+ ctx->disable_cmd_buf.buf_size, ctx->enabled);
+
+ if (!ctx->enabled)
+ return 0;
+
+ if (ctx->backlight) {
+ ctx->backlight->props.power = FB_BLANK_POWERDOWN;
+ backlight_update_status(ctx->backlight);
+ }
+#if 1
+ gpiod_set_value(ctx->gpio_backlight0, 0);
+#endif
+
+ ctx->enabled = false;
+ if (cmd_buf->buf_size > 0) {
+ if (cmd_buf->sleepUs) {
+ msleep(cmd_buf->sleepUs / 1000);
+ }
+ es_panel_dcs_write(ctx, cmd_buf->buf, cmd_buf->buf_size);
+ } else {
+ dev_warn(pr_dev, "%s[%d]:Disable cmd invalid,Please set it\n",
+ __func__, __LINE__);
+ }
+
+ dev_info(pr_dev, "[%s]display off... sleep in..\n", __func__);
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0)
+ dev_err(ctx->dev, "Failed to turn off the display: %d\n", ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0)
+ dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret);
+
+ return ret;
+}
+
+static int es_panel_unprepare(struct drm_panel *panel)
+{
+ struct es_panel *ctx = panel_to_es_panel(panel);
+
+ if (!ctx->prepared)
+ return 0;
+
+ ctx->error = 0;
+ dev_dbg(pr_dev, "%s:%d\n", __func__, __LINE__);
+ ctx->prepared = false;
+ return 0;
+}
+
+static int es_panel_prepare(struct drm_panel *panel)
+{
+ struct es_panel *ctx = panel_to_es_panel(panel);
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+ dev_dbg(pr_dev, "%s:%d\n", __func__, __LINE__);
+
+ // #ifdef CONFIG_1080P_4LANES_PANEL
+ es_panel_4_lanes_panel_init(ctx);
+ // #elif CONFIG_720P_3LANES_PANEL
+ // es_panel_3_lanes_panel_init(ctx);
+ // #else
+ // es_panel_panel_init(ctx);
+ // #endif
+
+ ret = ctx->error;
+ if (ret < 0)
+ es_panel_unprepare(panel);
+ ctx->prepared = true;
+#ifdef PANEL_SUPPORT_READBACK
+ es_panel_panel_get_data(ctx);
+#endif
+
+ return ret;
+}
+
+static int es_panel_enable(struct drm_panel *panel)
+{
+ struct es_panel *ctx = panel_to_es_panel(panel);
+ int ret = 0;
+ user_cmd_buffer_t *cmd_buf = &ctx->enable_cmd_buf;
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(panel->dev);
+
+ if (ctx->enabled)
+ return 0;
+
+ if (ctx->backlight) {
+ ctx->backlight->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(ctx->backlight);
+ }
+#if 1
+ gpiod_set_value(ctx->gpio_backlight0, 1);
+#endif
+
+ ctx->enabled = true;
+ if (cmd_buf->buf_size > 0) {
+ if (cmd_buf->sleepUs) {
+ msleep(cmd_buf->sleepUs / 1000);
+ }
+ es_panel_dcs_write(ctx, cmd_buf->buf, cmd_buf->buf_size);
+ } else {
+ dev_warn(pr_dev, "%s[%d]:Enable cmd invalid,Please set it\n",
+ __func__, __LINE__);
+ }
+
+ dev_info(pr_dev, "[%s] sleep out.., display on...\n", __func__);
+ msleep(20);
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ /* Panel is operational 120 msec after reset */
+ msleep(200);
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret)
+ return ret;
+ msleep(50);
+
+ return ret;
+}
+
+static int es_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ struct es_panel *ctx = panel_to_es_panel(panel);
+ struct drm_display_mode *mode;
+
+ dev_dbg(pr_dev, "%s:%d\n", __func__, __LINE__);
+ if (ctx->user_mode_inited == false) {
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
+ if (!mode) {
+ dev_err(panel->dev, "failed to add mode %ux%ux\n",
+ default_mode.hdisplay, default_mode.vdisplay);
+ return -ENOMEM;
+ }
+ } else {
+ mode = drm_mode_duplicate(connector->dev, &ctx->user_mode);
+ if (!mode) {
+ dev_err(panel->dev, "failed to add mode %ux%ux\n",
+ ctx->user_mode.hdisplay,
+ ctx->user_mode.vdisplay);
+ return -ENOMEM;
+ }
+ }
+ dev_info(
+ pr_dev,
+ "get modes user_mode_inited:%d, clock:%d, hdisplay:%u, hsyncStart:%u, hsyncEnd:%u, htotal:%u\n"
+ "vdisplay:%u vsyncStart:%u vsyncEnd:%u vtotal:%u width_mm:%d, height_mm:%d flags:0x%08x \n",
+ ctx->user_mode_inited, mode->clock, mode->hdisplay,
+ mode->hsync_start, mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start, mode->vsync_end,
+ mode->vtotal, mode->width_mm, mode->height_mm, mode->flags);
+
+ drm_mode_set_name(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = ctx->user_mode.width_mm;
+ connector->display_info.height_mm = ctx->user_mode.height_mm;
+ drm_display_info_set_bus_formats(&connector->display_info, &bus_format,
+ 1);
+ connector->display_info.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs es_panel_drm_funcs = {
+ .disable = es_panel_disable,
+ .unprepare = es_panel_unprepare,
+ .prepare = es_panel_prepare,
+ .enable = es_panel_enable,
+ .get_modes = es_panel_get_modes,
+};
+
+int es_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct es_panel *ctx;
+ int ret;
+ struct device_node *dsi_node, *remote_node = NULL, *endpoint = NULL;
+
+ // for print
+ pr_dev = dev;
+
+ dev_info(pr_dev, "[%s] Enter\n", __func__);
+
+ dsi_node = of_get_parent(dev->of_node);
+ if (dsi_node) {
+ endpoint = of_graph_get_next_endpoint(dsi_node, NULL);
+ if (endpoint) {
+ remote_node = of_graph_get_remote_port_parent(endpoint);
+ if (!remote_node) {
+ dev_err(pr_dev,
+ "No panel connected,skip probe es_panel\n");
+ return -ENODEV;
+ }
+ dev_info(pr_dev, "device node name:%s\n",
+ remote_node->name);
+ }
+ }
+
+ /*
+ if (remote_node != dev->of_node) {
+ dev_info(pr_dev,"%s+ skip probe due to not current es_panel\n", __func__);
+ return -ENODEV;
+ }
+ */
+ ctx = devm_kzalloc(dev, sizeof(struct es_panel), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mipi_dsi_set_drvdata(dsi, ctx);
+ ctx->dev = dev;
+
+ // default val, if other value, set it on:IOC_ES_MIPI_TX_SET_MODE.
+ dsi->lanes = LANES;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ // dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
+ MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+
+ ctx->gpio_backlight0 =
+ devm_gpiod_get(ctx->dev, "backlight0", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->gpio_backlight0)) {
+ dev_err(ctx->dev, "failed to get backlight0 gpio, err: %ld\n",
+ PTR_ERR(ctx->gpio_backlight0));
+ return PTR_ERR(ctx->gpio_backlight0);
+ }
+
+ ctx->gpio_reset = devm_gpiod_get(ctx->dev, "rst", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->gpio_reset)) {
+ dev_err(ctx->dev, "failed to get rst gpio, err: %ld\n",
+ PTR_ERR(ctx->gpio_reset));
+ return PTR_ERR(ctx->gpio_reset);
+ }
+ msleep(50);
+
+ gpiod_set_value(ctx->gpio_reset, 1);
+ gpiod_set_value(ctx->gpio_backlight0, 1);
+
+ ctx->prepared = false;
+ ctx->enabled = false;
+ ctx->user_mode_inited = false;
+
+ drm_panel_init(&ctx->panel, dev, &es_panel_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ // ctx->panel.dev = dev;
+ // ctx->panel.funcs = &es_panel_drm_funcs;
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ drm_panel_remove(&ctx->panel);
+
+ es_panel_chrdev_create(ctx);
+ INIT_LIST_HEAD(&ctx->init_cmd_list);
+ ctx->init_cmd_writted = 0;
+ memset(&ctx->enable_cmd_buf, 0, sizeof(user_cmd_buffer_t));
+ memset(&ctx->disable_cmd_buf, 0, sizeof(user_cmd_buffer_t));
+ return ret;
+}
+
+void es_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct es_panel *ctx = mipi_dsi_get_drvdata(dsi);
+
+ free_cmd_list(&ctx->init_cmd_list);
+ free_cmd_buf(&ctx->enable_cmd_buf);
+ free_cmd_buf(&ctx->disable_cmd_buf);
+
+ devm_gpiod_put(ctx->dev, ctx->gpio_backlight0);
+ devm_gpiod_put(ctx->dev, ctx->gpio_reset);
+
+ es_panel_chrdev_destroy(ctx);
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&ctx->panel);
+
+ return;
+}
+
+static const struct of_device_id es_panel_of_match[] = {
+ {
+ .compatible = "eswin,generic-panel",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es_panel_of_match);
+
+struct mipi_dsi_driver es_panel_driver = {
+ .probe = es_panel_probe,
+ .remove = es_panel_remove,
+ .driver =
+ {
+ .name = "es-panel",
+ .of_match_table = es_panel_of_match,
+ },
+};
+
+MODULE_AUTHOR("Eswin");
+MODULE_DESCRIPTION("Eswin Generic Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_panel.h b/drivers/gpu/drm/eswin/es_panel.h
new file mode 100644
index 000000000000..96ae0171f379
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_panel.h
@@ -0,0 +1,67 @@
+#ifndef _ES_PANEL_H__
+#define _ES_PANEL_H__
+
+#include <drm/drm_mipi_dsi.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+enum {
+ IOC_ES_MIPI_TX_SET_MODE = 0x100,
+ IOC_ES_MIPI_TX_SET_INIT_CMD,
+ IOC_ES_MIPI_TX_SET_CMD,
+ IOC_ES_MIPI_TX_ENABLE,
+ IOC_ES_MIPI_TX_DISABLE,
+};
+
+typedef enum MIPI_OUT_FORMAT_E {
+ MIPI_OUT_FORMAT_RGB_16_BIT = 0x0,
+ MIPI_OUT_FORMAT_RGB_18_BIT,
+ MIPI_OUT_FORMAT_RGB_24_BIT,
+ MIPI_OUT_FORMAT_BUTT
+} MIPI_OUT_FORMAT_E;
+
+typedef enum MIPI_OUT_MODE_E {
+ MIPI_OUTPUT_MODE_CSI = 0x0,
+ MIPI_OUTPUT_MODE_DSI_VIDEO,
+ MIPI_OUTPUT_MODE_DSI_CMD,
+ MIPI_OUTPUT_MODE_DSI_BUTT
+} MIPI_OUT_MODE_E;
+
+typedef struct MIPI_MODE_INFO {
+ u32 devId;
+ u16 hdisplay;
+ u16 hsyncStart;
+ u16 hsyncEnd;
+ u16 htotal;
+ u16 hskew;
+ u16 vdisplay;
+ u16 vsyncStart;
+ u16 vsyncEnd;
+ u16 vtotal;
+ u16 vscan;
+
+ u32 vrefresh;
+ u32 flags;
+ u32 type;
+
+ MIPI_OUT_FORMAT_E outputFormat;
+ MIPI_OUT_MODE_E outputMode;
+ u32 lanes;
+} MIPI_MODE_INFO_S;
+
+typedef struct MIPI_CMD_S {
+ int devId;
+ u16 dataType;
+ u16 cmdSize;
+ u32 sleepUs;
+ u8 *pCmd;
+} MIPI_CMD_S;
+
+extern struct mipi_dsi_driver es_panel_driver;
+void es_panel_remove(struct mipi_dsi_device *dsi);
+int es_panel_probe(struct mipi_dsi_device *dsi);
+
+#endif //_ES_PANEL_H__
\ No newline at end of file
--
2.47.0