“线程: 测试”中讨论的其它部件
大家好,我正在尝试将平板电脑显示面板连接到 sdm660处理器卡。 我使用 sn65dsi83将 DSI 转换为 LVDS,但无法使屏幕正常工作。 设置时,屏幕上会出现“测试彭定康”,但我不知道测试板的工作原理。 我希望屏幕正常工作。 请帮帮我。
From 305271b16da1aa37a11f09945d8f24c82cb9711a Mon Sep 17 00:00:00 2001
From: "rid.wang" <rid.wang@quectel.com>
Date: Wed, 3 Jul 2019 09:40:05 +0800
Subject: [PATCH] add the sn65dsi83 driver in kernel
Change-Id: I84808c464c7aa42d11205f19a2abee35f1dbba08
---
.../dts/qcom/dsi-panel-hx8394f-720p-video.dtsi | 51 +-
.../arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi | 10 +-
.../msm-4.4/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi | 32 +-
.../arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi | 25 +
kernel/msm-4.4/drivers/video/Makefile | 3 +
kernel/msm-4.4/drivers/video/mipi_convert.c | 629 +++++++++++++++++++++
6 files changed, 705 insertions(+), 45 deletions(-)
create mode 100755 kernel/msm-4.4/drivers/video/mipi_convert.c
diff --git a/kernel/msm-4.4/arch/arm/boot/dts/qcom/dsi-panel-hx8394f-720p-video.dtsi b/kernel/msm-4.4/arch/arm/boot/dts/qcom/dsi-panel-hx8394f-720p-video.dtsi
index 4ae9ad6..72c68d9 100755
--- a/kernel/msm-4.4/arch/arm/boot/dts/qcom/dsi-panel-hx8394f-720p-video.dtsi
+++ b/kernel/msm-4.4/arch/arm/boot/dts/qcom/dsi-panel-hx8394f-720p-video.dtsi
@@ -24,15 +24,15 @@
qcom,mdss-dsi-panel-framerate = <60>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
- qcom,mdss-dsi-panel-width = <720>;
- qcom,mdss-dsi-panel-height = <1280>;
- qcom,mdss-dsi-h-front-porch = <50>;
- qcom,mdss-dsi-h-back-porch = <50>;
- qcom,mdss-dsi-h-pulse-width = <50>;
+ qcom,mdss-dsi-panel-width = <1024>;
+ qcom,mdss-dsi-panel-height = <768>;
+ qcom,mdss-dsi-h-front-porch = <80>;
+ qcom,mdss-dsi-h-back-porch = <160>;
+ qcom,mdss-dsi-h-pulse-width = <80>;
qcom,mdss-dsi-h-sync-skew = <0>;
- qcom,mdss-dsi-v-back-porch = <4>;
- qcom,mdss-dsi-v-front-porch = <10>;
- qcom,mdss-dsi-v-pulse-width = <4>;
+ qcom,mdss-dsi-v-back-porch = <23>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <7>;
qcom,mdss-dsi-h-left-border = <0>;
qcom,mdss-dsi-h-right-border = <0>;
qcom,mdss-dsi-v-top-border = <0>;
@@ -41,28 +41,8 @@
qcom,mdss-dsi-color-order = "rgb_swap_rgb";
qcom,mdss-dsi-underflow-color = <0xff>;
qcom,mdss-dsi-border-color = <0>;
- qcom,mdss-dsi-on-command = [29 01 00 00 00 00 04 B9 FF 83 94
-29 01 00 00 00 00 07 BA 63 03 68 6b b2 c0
-29 01 00 00 00 00 0B B1 50 12 72 09 33 54 B1 31 6B 2F
-29 01 00 00 00 00 07 B2 00 80 64 0e 0d 2f
-29 01 00 00 00 00 16 B4 73 74 73 74 73 74 01 0C 86 75 00 3F 73 74 73 74 73 74 01 0C 86
-29 01 00 00 00 00 22 D3 00 00 07 07 40 07 10 00 08 10 08 00 08 54 15 0e 05 0e 02 15 06 05 06 47 44 0a 0a 4b 10 07 07 0e 40
-29 01 00 00 00 00 2D D5 1a 1a 1b 1b 00 01 02 03 04 05 06 07 08 09 0a 0b 24 25 18 18 26 27 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 20 21 18 18 18 18
-29 01 00 00 00 00 2D D6 1a 1a 1b 1b 0b 0a 09 08 07 06 05 04 03 02 01 00 21 20 18 18 27 26 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 25 24 18 18 18 18
-29 01 00 00 00 00 3B E0 00 0D 1B 22 25 2A 2F 2C 5A 6B 7A 77 7E 8E 92 95 9F 9E 99 a1 b0 57 55 5C 5F 5F 67 6F 7f 00 0D 1B 22 25 2A 2F 2C 5A 6B 7A 77 7E 8E 92 95 9F 9E 99 a1 b0 57 55 5C 5F 5F 67 6F 7f
-29 01 00 00 00 00 03 C0 1f 31
-29 01 00 00 00 00 02 CC 0B
-29 01 00 00 00 00 03 B6 78 78
-29 01 00 00 00 00 02 D4 02
-29 01 00 00 00 00 02 BD 02
-29 01 00 00 00 00 0D D8 FF FF FF FF FF FF FF FF FF FF FF FF
-29 01 00 00 00 00 02 BD 00
-29 01 00 00 00 00 02 BD 01
-29 01 00 00 00 00 02 B1 00
-29 01 00 00 00 00 02 BD 00
-29 01 00 00 00 00 08 BF 40 81 50 00 1A FC 01
-05 01 00 00 78 00 02 11 00
-05 01 00 00 05 00 02 29 00];
+ qcom,mdss-dsi-on-command = [05 01 00 00 78 00 02 11 00
+ 05 01 00 00 05 00 02 29 00];
qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00
05 01 00 00 78 00 02 10 00];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
@@ -71,19 +51,20 @@
qcom,mdss-dsi-traffic-mode = "burst_mode";
qcom,mdss-dsi-lane-map = "lane_map_0123";
qcom,mdss-dsi-bllp-eof-power-mode;
- qcom,mdss-dsi-bllp-power-mode;
+ //qcom,mdss-dsi-bllp-power-mode;
qcom,mdss-dsi-lane-0-state;
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
- qcom,mdss-dsi-panel-timings = [7B 1A 10 00 3C 40 14 1C 15 03 04 00];
- qcom,mdss-dsi-t-clk-post = <0x04>;
- qcom,mdss-dsi-t-clk-pre = <0x1A>;
+ qcom,mdss-dsi-panel-timings = [6D 18 10 00 3A 3E 12 1A 13 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x0a>;
+ qcom,mdss-dsi-t-clk-pre = <0x1c>;
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 60>;
+ qcom,mdss-dsi-force-clock-lane-hs;
};
};
diff --git a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 248782a..8eb7040 100755
--- a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -151,11 +151,11 @@
&dsi_hx8394f_720p_video {
qcom,mdss-dsi-panel-timings-phy-v2 = [
- 1e 1b 04 05 02 03 04 a0 /*Data 0*/
- 1e 1b 04 05 02 03 04 a0 /*Data 1*/
- 1e 1b 04 05 02 03 04 a0 /*Data 2*/
- 1e 1b 04 05 02 03 04 a0 /*Data 3*/
- 1e 0d 03 05 02 03 04 a0]; /*CLK lane*/
+ 1e 1a 04 05 02 02 04 a0 /*Data 0*/
+ 1e 1a 04 05 02 02 04 a0 /*Data 1*/
+ 1e 1a 04 05 02 02 04 a0 /*Data 2*/
+ 1e 1a 04 05 02 02 04 a0 /*Data 3*/
+ 1e 0d 03 05 02 02 04 a0]; /*CLK lane*/
};
&dsi_dual_nt35597_truly_video {
diff --git a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
index b0ab543..a6a17dc 100755
--- a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
+++ b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
@@ -23,7 +23,7 @@
//cwy-> add i2c1 and gt9xx
&i2c_1 { /* BLSP1 QUP1 */
status = "ok";
- goodix@5d {
+/* goodix@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
interrupt-parent = <&tlmm>;
@@ -80,7 +80,19 @@
pinctrl-names = "pmx_ts_active","pmx_ts_suspend";
pinctrl-0 = <&ts_int_active &ts_reset_active>;
pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
- };
+ };*/
+ mipi_convert@2c {
+ compatible = "qcom,mipi_convert";
+ reg = <0x2c>;
+ pinctrl-names = "pmx_convert_active",
+ "pmx_convert_suspend";
+ pinctrl-0 = <&convert_rst_active>;
+ pinctrl-1 = <&convert_rst_suspend>;
+ //backlight-en = <&tlmm 24 0x00>;
+ convert,rst-gpio = <&tlmm 53 0x00>; //reset or en gpio
+ //convert,irq-gpio = <&tlmm 13 0x00>;
+ //avdd-supply = <&pm8909_l6>;
+ };
};
@@ -131,6 +143,16 @@
qcom,src-sel = <0>;
qcom,out-strength = <1>;
};
+ /*jeffery modify gpio2 and gpio3 to adc 20190515 */
+ gpio@c100 {
+ qcom,master-en = <0>;
+ status = "ok";
+ };
+
+ gpio@c200 {
+ qcom,master-en = <0>;
+ status = "ok";
+ };
};
&i2c_6 { /* BLSP1 QUP6 (NFC) */
@@ -167,9 +189,9 @@
&mdss_dsi0 {
qcom,dsi-pref-prim-pan = <&dsi_hx8394f_720p_video>;
pinctrl-names = "mdss_default", "mdss_sleep";
- pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
- pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
- qcom,platform-reset-gpio = <&tlmm 53 0>;
+ pinctrl-0 = <&mdss_te_active>;
+ pinctrl-1 = <&mdss_te_suspend>;
+// qcom,platform-reset-gpio = <&tlmm 53 0>;
qcom,platform-te-gpio = <&tlmm 59 0>;
};
diff --git a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
index bba24ad..1b56cdd 100755
--- a/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
+++ b/kernel/msm-4.4/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
@@ -1762,6 +1762,31 @@
};
ts_mux {
+ convert_rst_active: convert_bk_active {
+ mux {
+ pins = "gpio53";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio53";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+ convert_rst_suspend: convert_bk_suspend {
+ mux {
+ pins = "gpio53";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio53";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
ts_reset_active: ts_reset_active {
mux {
pins = "gpio66";
diff --git a/kernel/msm-4.4/drivers/video/Makefile b/kernel/msm-4.4/drivers/video/Makefile
index 0a19066..816e295 100644
--- a/kernel/msm-4.4/drivers/video/Makefile
+++ b/kernel/msm-4.4/drivers/video/Makefile
@@ -13,3 +13,6 @@ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
endif
+
+#kyle sn65dsi84
+obj-y += mipi_convert.o
diff --git a/kernel/msm-4.4/drivers/video/mipi_convert.c b/kernel/msm-4.4/drivers/video/mipi_convert.c
new file mode 100755
index 0000000..9aa1e85
--- /dev/null
+++ b/kernel/msm-4.4/drivers/video/mipi_convert.c
@@ -0,0 +1,629 @@
+#define DEBUG
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+
+//#define CONVERT_DEBUG
+
+#define PINCTRL_STATE_ACTIVE "pmx_convert_active"
+#define PINCTRL_STATE_SUSPEND "pmx_convert_suspend"
+
+//#define XBL_INIT
+
+enum convert_i2c_addr {
+ I2C_ADDR_MAIN = 0x2c,
+};
+
+struct convert_platform_data {
+ struct i2c_client *client;
+ struct pinctrl *convert_pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ u32 irq_gpio;
+ u32 irq_flags;
+ u32 rst_gpio;
+ u32 rst_flags;
+ u32 backlight_en;
+ u32 backlight_en_flags;
+ struct regulator *avdd;
+#ifdef CONVERT_DEBUG
+ struct delayed_work convert_check_hpd_work_id;
+ struct workqueue_struct *workq;
+#endif
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#endif
+};
+
+struct convert_reg_cfg {
+ u8 i2c_addr;
+ u8 reg;
+ u8 val;
+ int sleep_in_ms;
+};
+#if 1
+static struct convert_reg_cfg convert_init_setup[] = { //1024/50fps
+{I2C_ADDR_MAIN, 0x09, 0x00, 2},
+{I2C_ADDR_MAIN, 0x0A, 0x04, 2},
+{I2C_ADDR_MAIN, 0x0B, 0x00, 2},
+{I2C_ADDR_MAIN, 0x0D, 0x00, 2},
+{I2C_ADDR_MAIN, 0x10, 0x26, 2},
+{I2C_ADDR_MAIN, 0x11, 0x00, 2},
+{I2C_ADDR_MAIN, 0x12, 0x27, 2},
+{I2C_ADDR_MAIN, 0x13, 0x00, 2},
+{I2C_ADDR_MAIN, 0x18, 0x78, 2},
+{I2C_ADDR_MAIN, 0x19, 0x00, 2},
+{I2C_ADDR_MAIN, 0x1A, 0x03, 2},
+{I2C_ADDR_MAIN, 0x1B, 0x00, 2},
+{I2C_ADDR_MAIN, 0x20, 0x00, 2},
+{I2C_ADDR_MAIN, 0x21, 0x04, 2},
+{I2C_ADDR_MAIN, 0x22, 0x00, 2},
+{I2C_ADDR_MAIN, 0x23, 0x00, 2},
+{I2C_ADDR_MAIN, 0x24, 0x00, 2},
+{I2C_ADDR_MAIN, 0x25, 0x00, 2},
+{I2C_ADDR_MAIN, 0x26, 0x00, 2},
+{I2C_ADDR_MAIN, 0x27, 0x00, 2},
+{I2C_ADDR_MAIN, 0x28, 0x20, 2},
+{I2C_ADDR_MAIN, 0x29, 0x00, 2},
+{I2C_ADDR_MAIN, 0x2A, 0x00, 2},
+{I2C_ADDR_MAIN, 0x2B, 0x00, 2},
+{I2C_ADDR_MAIN, 0x2C, 0x50, 2},
+{I2C_ADDR_MAIN, 0x2D, 0x00, 2},
+{I2C_ADDR_MAIN, 0x2E, 0x00, 2},
+{I2C_ADDR_MAIN, 0x2F, 0x00, 2},
+{I2C_ADDR_MAIN, 0x30, 0x07, 2},
+{I2C_ADDR_MAIN, 0x31, 0x00, 2},
+{I2C_ADDR_MAIN, 0x32, 0x00, 2},
+{I2C_ADDR_MAIN, 0x33, 0x00, 2},
+{I2C_ADDR_MAIN, 0x34, 0xa0, 2},
+{I2C_ADDR_MAIN, 0x35, 0x00, 2},
+{I2C_ADDR_MAIN, 0x36, 0x00, 2},
+{I2C_ADDR_MAIN, 0x37, 0x00, 2},
+{I2C_ADDR_MAIN, 0x38, 0x00, 2},
+{I2C_ADDR_MAIN, 0x39, 0x00, 2},
+{I2C_ADDR_MAIN, 0x3A, 0x00, 2},
+{I2C_ADDR_MAIN, 0x3B, 0x00, 2},
+{I2C_ADDR_MAIN, 0x3C, 0x00, 2},
+{I2C_ADDR_MAIN, 0x3D, 0x00, 2},
+{I2C_ADDR_MAIN, 0x3E, 0x00, 5},
+
+//{I2C_ADDR_MAIN, 0xE0, 0x00, 1},
+{I2C_ADDR_MAIN, 0x0D, 0x01, 10}, //PLL enable
+{I2C_ADDR_MAIN, 0x09, 0x01, 0}, //soft reset
+};
+#else //test 60fps
+static struct convert_reg_cfg convert_init_setup[] = {
+{I2C_ADDR_MAIN, 0x09, 0x00, 0},
+{I2C_ADDR_MAIN, 0x0A, 0x03, 0},
+{I2C_ADDR_MAIN, 0x0B, 0x20, 0},
+{I2C_ADDR_MAIN, 0x0D, 0x00, 0},
+{I2C_ADDR_MAIN, 0x10, 0x26, 0},
+{I2C_ADDR_MAIN, 0x11, 0x00, 0},
+{I2C_ADDR_MAIN, 0x12, 0x2b, 0},
+{I2C_ADDR_MAIN, 0x13, 0x00, 0},
+{I2C_ADDR_MAIN, 0x18, 0x6c, 0},
+{I2C_ADDR_MAIN, 0x19, 0x00, 0},
+{I2C_ADDR_MAIN, 0x1A, 0x03, 0},
+{I2C_ADDR_MAIN, 0x1B, 0x00, 0},
+{I2C_ADDR_MAIN, 0x20, 0x80, 0},
+{I2C_ADDR_MAIN, 0x21, 0x02, 0},
+{I2C_ADDR_MAIN, 0x22, 0x00, 0},
+{I2C_ADDR_MAIN, 0x23, 0x00, 0},
+{I2C_ADDR_MAIN, 0x24, 0x00, 0},
+{I2C_ADDR_MAIN, 0x25, 0x04, 0},
+{I2C_ADDR_MAIN, 0x26, 0x00, 0},
+{I2C_ADDR_MAIN, 0x27, 0x00, 0},
+{I2C_ADDR_MAIN, 0x28, 0xa7, 0},
+{I2C_ADDR_MAIN, 0x29, 0x00, 0},
+{I2C_ADDR_MAIN, 0x2A, 0x00, 0},
+{I2C_ADDR_MAIN, 0x2B, 0x00, 0},
+{I2C_ADDR_MAIN, 0x2C, 0x06, 0}, //HPW_L
+{I2C_ADDR_MAIN, 0x2D, 0x00, 0}, //HPW_H
+{I2C_ADDR_MAIN, 0x2E, 0x00, 0},
+{I2C_ADDR_MAIN, 0x2F, 0x00, 0},
+{I2C_ADDR_MAIN, 0x30, 0x04, 0}, //VPW_L
+{I2C_ADDR_MAIN, 0x31, 0x00, 0}, //VPW_H
+{I2C_ADDR_MAIN, 0x32, 0x00, 0},
+{I2C_ADDR_MAIN, 0x33, 0x00, 0},
+{I2C_ADDR_MAIN, 0x34, 0x17, 0}, //HBP
+{I2C_ADDR_MAIN, 0x35, 0x00, 0},
+{I2C_ADDR_MAIN, 0x36, 0x09, 0}, //test VBP
+{I2C_ADDR_MAIN, 0x37, 0x00, 0},
+{I2C_ADDR_MAIN, 0x38, 0x17, 0}, //test HFP
+{I2C_ADDR_MAIN, 0x39, 0x00, 0},
+{I2C_ADDR_MAIN, 0x3A, 0x09, 0}, //test VFP
+{I2C_ADDR_MAIN, 0x3B, 0x00, 0},
+{I2C_ADDR_MAIN, 0x3C, 0x10, 0},
+{I2C_ADDR_MAIN, 0x3D, 0x00, 0},
+{I2C_ADDR_MAIN, 0x3E, 0x00, 5},
+
+{I2C_ADDR_MAIN, 0x0D, 0x01, 8}, //PLL enable
+{I2C_ADDR_MAIN, 0x09, 0x01, 0}, //soft reset
+};
+#endif
+
+static int convert_i2c_write8(struct i2c_client *client, u8 addr, u8 reg, u8 value)
+{
+ int ret = 0;
+ client->addr = addr;
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+ if(ret < 0)
+ dev_err(&client->dev, "I2C write reg:%x error\n", reg);
+ return ret;
+}
+
+static int convert_i2c_read8(struct i2c_client *client, u8 addr, u8 reg, u8 *value)
+{
+ int ret = 0;
+ client->addr = addr;
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read reg:%x error\n", reg);
+ return ret;
+ }
+ *value = (u8)ret;
+ dev_dbg(&client->dev, "reg: %x, READ8: %x\n", reg, *value);
+ return 0;
+}
+
+static void convert_read_array(struct i2c_client *client,
+ struct convert_reg_cfg *cfg, int size)
+{
+ int ret = 0;
+ int i;
+ u8 value;
+
+ size = size / sizeof(struct convert_reg_cfg);
+ for (i = 0; i < size; i++) {
+ ret = convert_i2c_read8(client, cfg[i].i2c_addr,
+ cfg[i].reg, &value);
+ if (cfg[i].sleep_in_ms)
+ msleep(cfg[i].sleep_in_ms);
+ }
+}
+#ifdef XBL_INIT
+static void convert_write_array(struct i2c_client *client,
+ struct convert_reg_cfg *cfg, int size)
+{
+ int ret = 0;
+ int i;
+
+ size = size / sizeof(struct convert_reg_cfg);
+ for (i = 0; i < size; i++) {
+ ret = convert_i2c_write8(client, cfg[i].i2c_addr,
+ cfg[i].reg, cfg[i].val);
+ if (ret != 0){
+ dev_err(&client->dev, "%s: convert reg write %02X to %02X failed.\n",
+ __func__, cfg[i].val, cfg[i].reg);
+ }
+ if (cfg[i].sleep_in_ms)
+ msleep(cfg[i].sleep_in_ms);
+ }
+}
+#endif
+static int convert_gpio_configure(struct convert_platform_data *pdata, bool on)
+{
+ int ret = 0;
+ if (on) {
+ if (gpio_is_valid(pdata->rst_gpio)) {
+ ret = gpio_request(pdata->rst_gpio, "convert_rst_gpio");
+ if (ret) {
+ dev_err(&pdata->client->dev, "%d unable to request gpio [%d] ret=%d\n",
+ __LINE__, pdata->rst_gpio, ret);
+ goto err_none;
+ }
+ ret = gpio_direction_output(pdata->rst_gpio, 1);
+ if (ret) {
+ dev_err(&pdata->client->dev, "unable to set dir for gpio[%d]\n",
+ pdata->rst_gpio);
+ goto err_rst_gpio;
+ }
+#ifdef XBL_INIT
+ gpio_set_value_cansleep(pdata->rst_gpio, 0);
+ mdelay(20);
+#endif
+ gpio_set_value_cansleep(pdata->rst_gpio, 1);
+ mdelay(20);
+ } else {
+ dev_err(&pdata->client->dev, "rst gpio not provided\n");
+ goto err_none;
+ }
+
+ if (gpio_is_valid(pdata->backlight_en)) {
+ ret = gpio_request(pdata->backlight_en, "backlight_en");
+ if (ret) {
+ dev_err(&pdata->client->dev, "%d unable to backlight_en gpio [%d] ret=%d\n",
+ __LINE__, pdata->backlight_en, ret);
+ goto err_rst_gpio;
+ }
+ ret = gpio_direction_output(pdata->backlight_en, 1);
+ if (ret) {
+ dev_err(&pdata->client->dev, "unable to set dir for gpio[%d]\n",
+ pdata->backlight_en);
+ goto err_bk_gpio;
+ }
+ } else {
+ dev_err(&pdata->client->dev, "backlight_en gpio not provided\n");
+ }
+
+ if (gpio_is_valid(pdata->irq_gpio)) {
+ ret = gpio_request(pdata->irq_gpio, "convert_irq_gpio");
+ if (ret) {
+ dev_err(&pdata->client->dev,"%d unable to request gpio [%d] ret=%d\n",
+ __LINE__, pdata->irq_gpio, ret);
+ goto err_bk_gpio;
+ }
+ ret = gpio_direction_input(pdata->irq_gpio);
+ if (ret) {
+ dev_err(&pdata->client->dev, "unable to set dir for gpio[%d]\n",
+ pdata->irq_gpio);
+ goto err_irq_gpio;
+ }
+ } else {
+ dev_err(&pdata->client->dev, "irq gpio not provided\n");
+ }
+
+ return 0;
+ } else {
+ if (gpio_is_valid(pdata->rst_gpio))
+ gpio_free(pdata->rst_gpio);
+ if (gpio_is_valid(pdata->backlight_en))
+ gpio_free(pdata->backlight_en);
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+
+ return 0;
+ }
+
+err_irq_gpio:
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+err_bk_gpio:
+ if (gpio_is_valid(pdata->backlight_en))
+ gpio_free(pdata->backlight_en);
+err_rst_gpio:
+ if (gpio_is_valid(pdata->rst_gpio))
+ gpio_free(pdata->rst_gpio);
+err_none:
+ return ret;
+}
+
+static int convert_power_configure(struct convert_platform_data *pdata, bool on)
+{
+ int ret = 0;
+ if(on){
+ //pdata->avdd = regulator_get_optional(&pdata->client->dev, "avdd");
+ pdata->avdd = regulator_get(&pdata->client->dev, "avdd");
+ if (IS_ERR(pdata->avdd)) {
+ ret = PTR_ERR(pdata->avdd);
+ dev_err(&pdata->client->dev,
+ "Regulator get failed avdd ret=%d\n", ret);
+ }
+ }else{
+ if(!IS_ERR(pdata->avdd)){
+ regulator_put(pdata->avdd);
+ }
+ }
+ return ret;
+}
+
+static int convert_power_on(struct convert_platform_data *pdata)
+{
+ int ret = 0;
+ if (!IS_ERR(pdata->avdd)) {
+ ret = regulator_enable(pdata->avdd);
+ if (ret) {
+ dev_err(&pdata->client->dev,
+ "Regulator avdd enable failed ret=%d\n", ret);
+ }
+ }
+ return ret;
+}
+
+static int convert_power_off(struct convert_platform_data *pdata)
+{
+ int ret;
+ if (!IS_ERR(pdata->avdd)) {
+ ret = regulator_disable(pdata->avdd);
+ if (ret) {
+ dev_err(&pdata->client->dev,
+ "Regulator avdd enable failed ret=%d\n", ret);
+ }
+ }
+ return ret;
+}
+
+static int convert_parse_dt(struct device *dev,
+ struct convert_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ int ret = 0;
+ /* Get pinctrl if target uses pinctrl */
+ pdata->convert_pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR_OR_NULL(pdata->convert_pinctrl)) {
+ dev_err(dev, "%s: Pincontrol DT property failed\n", __func__);
+ }else{
+ pdata->pinctrl_state_active = pinctrl_lookup_state(pdata->convert_pinctrl,
+ "pmx_convert_active");
+ if (IS_ERR_OR_NULL(pdata->pinctrl_state_active)) {
+ dev_err(dev, "%s:Can not lookup pmx_convert_active pinstate %ld\n", __func__,
+ PTR_ERR(pdata->pinctrl_state_active));
+ }
+
+ pdata->pinctrl_state_suspend = pinctrl_lookup_state(pdata->convert_pinctrl,
+ "pmx_convert_suspend");
+ if (IS_ERR_OR_NULL(pdata->pinctrl_state_suspend)) {
+ dev_err(dev, "%s: Can not lookup pmx_convert_suspend pinstate %ld\n", __func__,
+ PTR_ERR(pdata->pinctrl_state_suspend));
+ }
+ }
+ pdata->irq_gpio = of_get_named_gpio_flags(np,
+ "convert,irq-gpio", 0, &pdata->irq_flags);
+ if(pdata->irq_gpio < 0){
+ dev_err(dev,"%s: not find convert,irq-gpio\n", __func__);
+ }
+
+ pdata->backlight_en = of_get_named_gpio_flags(np,
+ "backlight-en", 0, &pdata->backlight_en_flags);
+ if(pdata->backlight_en < 0){
+ dev_err(dev,"%s: not find backlight-en\n", __func__);
+ }
+
+ pdata->rst_gpio = of_get_named_gpio_flags(np,
+ "convert,rst-gpio", 0, &pdata->rst_flags);
+ if(pdata->rst_gpio < 0){
+ dev_err(dev,"%s: not find convert,rst-gpio\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void convert_init(struct i2c_client *client)
+{
+#ifdef XBL_INIT
+ convert_write_array(client, convert_init_setup, sizeof(convert_init_setup));
+ msleep(1000);
+#endif
+ convert_read_array(client, convert_init_setup, sizeof(convert_init_setup));
+}
+
+static int convert_resume(struct device *dev)
+{
+ struct convert_platform_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ convert_i2c_write8(client, I2C_ADDR_MAIN, 0x0D, 0x01); //PLL enable
+ msleep(10);
+ convert_i2c_write8(client, I2C_ADDR_MAIN, 0x09, 0x01); //soft reset
+
+ if (gpio_is_valid(pdata->backlight_en))
+ gpio_set_value_cansleep(pdata->backlight_en, 1);
+ return 0;
+}
+
+static int convert_suspend(struct device *dev) {
+ struct convert_platform_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ convert_i2c_write8(client, I2C_ADDR_MAIN, 0x0D, 0x00); //PLL disable
+ msleep(10);
+ convert_i2c_write8(client, I2C_ADDR_MAIN, 0x09, 0x00); //soft reset
+
+ if (gpio_is_valid(pdata->backlight_en))
+ gpio_set_value_cansleep(pdata->backlight_en, 0);
+ return 0;
+}
+
+static int convert_remove(struct i2c_client *client) {
+ struct convert_platform_data *pdata = i2c_get_clientdata(client);
+ convert_power_off(pdata);
+ convert_power_configure(pdata, false);
+ convert_gpio_configure(pdata, false);
+
+#ifdef CONVERT_DEBUG
+ cancel_delayed_work_sync(&pdata->convert_check_hpd_work_id);
+ flush_workqueue(pdata->workq);
+ destroy_workqueue(pdata->workq);
+#endif
+#if defined(CONFIG_FB)
+ if(fb_unregister_client(&pdata->fb_notif))
+ dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#endif
+ devm_kfree(&client->dev, pdata);
+ return 0;
+}
+
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct convert_platform_data *pdata =
+ container_of(self, struct convert_platform_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ pdata && pdata->client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ convert_resume(&pdata->client->dev);
+ else if (*blank == FB_BLANK_POWERDOWN)
+ convert_suspend(&pdata->client->dev);
+ }
+
+ return 0;
+}
+
+#endif
+
+#ifdef CONVERT_DEBUG
+static void convert_check_hpd_work(struct work_struct *work)
+{
+ struct convert_platform_data *pdata;
+ struct delayed_work *dw = to_delayed_work(work);
+ u8 reg_val = 0;
+
+ pdata = container_of(dw, struct convert_platform_data,
+ convert_check_hpd_work_id);
+ if (!pdata) {
+ dev_err(&pdata->client->dev, "%s: invalid input\n", __func__);
+ return;
+ }
+
+ convert_i2c_read8(pdata->client, I2C_ADDR_MAIN, 0xE5, ®_val);
+ convert_i2c_write8(pdata->client, I2C_ADDR_MAIN, 0xE5, 0xff);
+
+ queue_delayed_work(pdata->workq, &pdata->convert_check_hpd_work_id, 500);
+}
+#endif
+
+static int convert_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct convert_platform_data *pdata;
+
+ if(client->dev.of_node){
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct convert_platform_data), GFP_KERNEL);
+ if(!pdata) {
+ dev_err(&client->dev, "Failed to allocate memory for convert_platform_data\n");
+ return -ENOMEM;
+ }
+
+ ret = convert_parse_dt(&client->dev, pdata);
+ if(ret){
+ dev_err(&client->dev, "%s: DT parsing failed\n", __func__);
+ goto err_dt_parse;
+ }
+ } else {
+ pdata = client->dev.platform_data;
+ }
+
+ if(!pdata) {
+ dev_err(&client->dev, "Invalid pdata\n");
+ return -EINVAL;
+ }
+
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ pdata->client = client;
+ if(!IS_ERR_OR_NULL(pdata->convert_pinctrl)){
+ ret = pinctrl_select_state(pdata->convert_pinctrl,
+ pdata->pinctrl_state_active);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: Failed to select %s pinstate %d\n",
+ __func__, PINCTRL_STATE_ACTIVE, ret);
+ }
+
+ ret = convert_gpio_configure(pdata, true);
+ if(ret){
+ pr_err("%s: Failed to configure GPIOs\n", __func__);
+ goto err_dt_parse;
+ }
+
+ ret = convert_power_configure(pdata, true);
+ if(ret != 0){
+ goto gpio_free;
+ }
+
+ ret = convert_power_on(pdata);
+ if(ret != 0){
+ goto power_free;
+ }
+
+ i2c_set_clientdata(client, pdata);
+ dev_err(&client->dev,"convert IC probe success\n");
+ dev_err(&client->dev,"init convert IC ...\n");
+ convert_init(client);
+
+#ifdef CONVERT_DEBUG
+ pdata->workq = create_workqueue("convert_workq");
+ if (!pdata->workq) {
+ dev_err(&client->dev, "%s: workqueue creation failed.\n", __func__);
+ ret = -EPERM;
+ goto err_workqueue;
+ }
+ INIT_DELAYED_WORK(&pdata->convert_check_hpd_work_id, convert_check_hpd_work); //there is issue in irq, so we use work queue to read connect status
+ queue_delayed_work(pdata->workq, &pdata->convert_check_hpd_work_id, 5 * 100);
+#endif
+
+#if defined(CONFIG_FB)
+ pdata->fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&pdata->fb_notif);
+ if(ret){
+ dev_err(&client->dev, "Unable to register fb_notifier: %d\n", ret);
+ }
+#endif
+ return 0;
+
+#ifdef CONVERT_DEBUG
+err_workqueue:
+ convert_power_off(pdata);
+#endif
+power_free:
+ convert_power_configure(pdata, false);
+gpio_free:
+ convert_gpio_configure(pdata, false);
+err_dt_parse:
+ devm_kfree(&client->dev, pdata);
+ return ret;
+}
+
+static const struct of_device_id convert_match_table[] = {
+ { .compatible = "qcom,mipi_convert",},
+ {},
+};
+
+static const struct i2c_device_id convert_id[] = {
+ {"mipi_convert", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, convert_id);
+
+static const struct dev_pm_ops convert_pm_ops =
+{
+ .suspend = convert_suspend,
+ .resume = convert_resume,
+};
+
+static struct i2c_driver convert_driver = {
+ .driver = {
+ .name = "mipi_convert",
+ .owner = THIS_MODULE,
+ .of_match_table = convert_match_table,
+ .pm = &convert_pm_ops,
+ },
+ .probe = convert_probe,
+ .remove = convert_remove,
+ .id_table = convert_id,
+};
+
+module_i2c_driver(convert_driver);
+
+MODULE_AUTHOR("Kyle <kyle.gao@quectel.com>");
+MODULE_DESCRIPTION("MIPI CONVERT driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
--
2.7.4
e2e.ti.com/.../CSR-_2800_3_2900_.txt
%CHIP1%PVCA%RPCA1280%RLCA800%PVCB%PMCBB%RMCB%RLCB%LVCM0%HPWA54%HPA52%HACA1280%HTOACA0%HPWB%HBPB%HBPB%HACB%HACB%0%HVP0%W0%WAC0%B%1%B%WAC%WAD%1%B%WPLAC0%B%VAC0%B%W0%BAD%1%WPL%BAD%BADHR%BADHR%1%ZHPLAD%1%BAD%1%HPLAD%1%HPLAC0%BAD%1%HPLAD%1%BAD%1%BAD%1%HPLANBAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%BAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%HPLAD%1%BAD%1%HPLAD%1%HPLANB















此照片 LVDS 时钟正负极。对吗?