Hello, I'm trying to connect a tablet display panel to a sdm660 processor card. I use sn65dsi83 to convert dsi to lvds, but I couldn't get the screen to work. Test patten appears on the screen when I make the settings, but I don't know why the test patttern works. I want the screen to work normally. Can you please help?
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
//===================================================================== // Filename : CSR.txt // // (C) Copyright 2013 by Texas Instruments Incorporated. // All rights reserved. // //===================================================================== 0x09 0x00 0x0A 0x64 0x0B 0x02 0x0D 0x01 0x10 0x20 0x11 0x00 0x12 0x00 0x13 0x00 0x18 0x78 0x19 0x00 0x1A 0x03 0x1B 0x00 0x20 0x00 0x21 0x00 0x22 0x00 0x23 0x00 0x24 0x00 0x25 0x00 0x26 0x00 0x27 0x00 0x28 0x20 0x29 0x00 0x2A 0x00 0x2B 0x00 0x2C 0x00 0x2D 0x00 0x2E 0x00 0x2F 0x00 0x30 0x00 0x31 0x00 0x32 0x00 0x33 0x00 0x34 0x00 0x35 0x00 0x36 0x00 0x37 0x00 0x38 0x00 0x39 0x00 0x3A 0x00 0x3B 0x00 0x3C 0x00 0x3D 0x00 0x3E 0x00 The PLL_EN bit and SOFT_RESET bit are not set as they need to be set per the recommended sequence defined in the datasheet
%CHIP1%PVCA%PMCA%RPCA1280%RLCA800%PVCB%PMCB%RPCB%RLCB%LVCM0%HPWA54%HBPA54%HFPA52%HACA1280%HTOA1440%HPWB%HBPB%HFPB%HACB%HTOB0%VPWA8%VBPA8%VFPA7%VACA800%VTOA823%VPWB%VBPB%VFPB%VACB%VTOB0%PCKN71.1%LCKS1%RCKM24%MULT2%DCKA%DIVI0%LCKR72.0%FMTA1%DEPA0%HSPA1%VSPA1%BPPA1%FMTB1%DEPB0%HSPB1%VSPB1%BPPB0%PRDA1280x800%PRDBx%DSCM0%LREO1%LRCE1%LPCA0%BMCA0%SMCA0%LPCB0%BMCB0%SMCB0%DHPA54%DHBA54%DHFA52%DHAA1280%DHTA1440%DHPB%DHBB%DHFB%DHAB%DHTB%DVPA8%DVBA8%DVFA7%DVAA800%DVTA823%DVPB%DVBB%DVFB%DVAB%DVTB%DDRA213.3%NOLA3%VIMA2%LCRP%DDRB%NOLB0%VIMB0%RCRP