“线程: 测试”中讨论的其它部件
大家好,我正在尝试将平板电脑显示面板连接到 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.4e2e.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