1904 lines
56 KiB
Diff
1904 lines
56 KiB
Diff
From 33e0fddf9766468c197d775ff0bf14ca0b59733e Mon Sep 17 00:00:00 2001
|
|
From: lilijun <lilijun@eswincomputing.com>
|
|
Date: Wed, 22 May 2024 14:27:49 +0800
|
|
Subject: [PATCH 029/222] feat(DRM): Add DSI support for linux 6.6
|
|
|
|
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
|
|
|