This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

SN65DSI86: LOSS_OF_DP_SYNC_LOCK_ERR

Part Number: SN65DSI86

Tool/software:

my customer is using SN65DSI86 on Qualcomm SM6225 platform, with zhongshen EDP display. customer found sometimes the display has flicker issue, checked SN65DSI86 and found 0xF6 is 40, which is LOSS_OF_DP_SYNC_LOCK_ERR, could you please help give more info/guidance what would cause this error?

Stone

  • Stone

    Sometimes errors flags can be set at power-on or during start of the DSI stream. For this reason, it is recommended to clear flags by writing 0xFF and then reading back status flags. The bits which remain set are the errors which should be focused on.

    Address 0xF6 thru 0xF7 report errors associated with DSI to DP video timing. Typically, errors are set in these registers when video timing programmed into DSI86 doesn’t match timing received on the DSI interface. It is important the DSI86’s video registers located from 0x20 thru 0x3A match video timing used by the DSI source. The DSI86 will derive the DP timings from values programmed into these registers.

    Can they please share their eDP display spec and DSI86 register programming values?

    Please also map HSYNC and VSYNC to the DSI86 GPIO pins using register 0x5F and measuring HSYNC and VSYNC frequency, do they meet the eDP display requirement?

    Thanks

    David

  • hi David :

    DSI86 register programming values  and eDP display spec

    /*
        Lontium SN65DSI86 MIPI to EDP driver
    
        Copyright  (C)  2016 - 2017 Topband. Ltd.
    
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2 of the License, or
        (at your option) any later version.
    
        This program is distributed in the hope that it will be a reference
        to you, when you are integrating the Lontium's SN65DSI86 IC into your system,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        General Public License for more details.
    
        Notes:
        1. IIC address of SN65DSI86:
         A) If SN65DSI86EXB's number 31 (S_ADR) is low, the I2C address of SN65DSI86EXB is 0x52,
            and bit0 is the read-write mark bit.
            If it is a Linux system, the I2C address of SN65DSI86EXB is 0x29, and bit7 is
            the read and write flag bit.
         B) If SN65DSI86EXB's number 31 (S_ADR) is high, then SN65DSI86EXB's I2C address is 0x5a,
            and bit0 is the read-write mark bit.
            If it is a Linux system, the I2C address of SN65DSI86EXB is 0x2d, and bit7 is
            the read and write flag bit.
        2. The IIC rate should not exceed 100KHz.
        3. To ensure that MIPI signal is given to SN65DSI86EXB, then initialize SN65DSI86EXB.
        4. The front-end master control GPIO is required to reply to SN65DSI86EXB. Before
          the register, the SN65DSI86EXB is reset.
          Use GPIO to lower SN65DSI86EXB's reset foot 100ms, then pull up and maintain 100ms.
        5. SN65DSI86EXB MIPI input signal requirements:
         A) MIPI DSI
         B) Video mode
         C) non-burst mode (continue mode) - (CLK of MIPI is continuous).
         D) sync event
    
        Author: shenhaibo
        Version: 1.1.0
        Release Date: 2019/3/6
    */
    #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/component.h>
    #include <linux/workqueue.h>
    #include <linux/of_gpio.h>
    #include <linux/of_graph.h>
    #include <linux/of_irq.h>
    #include <linux/regulator/consumer.h>
    #include <linux/firmware.h>
    #include <linux/hdmi.h>
    #include <drm/drm_print.h>
    #include <drm/drm_atomic.h>
    #include <drm/drm_atomic_helper.h>
    #include <drm/drm_edid.h>
    #include <drm/drm_mipi_dsi.h>
    #include <drm/drm_crtc_helper.h>
    #include <drm/drm_probe_helper.h>
    #include <drm/drm_bridge.h>
    #include <drm/drm_file.h>
    #include <drm/drm_device.h>
    #include <linux/string.h>
    #include "sn65dsi86.h"
    #include <linux/of.h>
    #include <drm/drm_panel.h>
    
    extern int get_display_panel_type(void);
    
    #define EDP_I2C_ADDR 0x2c
    
    #define SN65DSI86_DEVID
    #define CONFIG_QCOM_DRM           /****** simon add for suspend resume  ****/
    
    struct sn65dsi86_data *pSn65dsi86_data =NULL;
    
    #ifdef SN65DSI86_NODE
    static struct kobject *edp_kobj;
    #endif
    
    #ifdef CONFIG_QCOM_DRM
    static int sn65dsi86_resume_init(struct sn65dsi86_data *pdata);
    static int sn65dsi86_suspend_init(struct sn65dsi86_data *pdata);
    static int sn65dsi86_vstream_enable_off(struct sn65dsi86_data *pdata);
    
    static struct drm_panel *active_sn65dsi86_panel;
    static void sn65dsi86_panel_notifier_callback(enum panel_event_notifier_tag tag,
            struct panel_event_notification *event, void *client_data);
    
    struct drm_panel *sn65dsi86_get_panel(void)
    {
        return active_sn65dsi86_panel;
    }
    #endif
    
    struct i2c_client *sn65dsi86_client;
    
    /*******************************************************
        Function:
        Write data to the i2c slave device.
        Input:
        client: i2c device.
        buf[0]: write start address.
        buf[1~len-1]: data buffer
        len: SN65DSI86_ADDR_LENGTH + write bytes count
        Output:
        numbers of i2c_msgs to transfer:
            0: succeed, otherwise: failed
     *********************************************************/
    int sn65dsi86_i2c_write(struct i2c_client *client,unsigned short i2c_addr, u8 *buf, int len)
    {
        unsigned int pos = 0, transfer_length = 0;
        u8 address = buf[0];
        unsigned char put_buf[64];
        int retry, ret = 0;
        struct i2c_msg msg = {
            .addr = i2c_addr,
            .flags = !I2C_M_RD,
        };
        //dev_err(&client->dev, "dev:%02x,buf[0]:%02x,buf[1]:%02x,\n",i2c_addr,buf[0],buf[1]);
        if(likely(len < sizeof(put_buf))) {
            /* code optimize,use stack memory*/
            msg.buf = &put_buf[0];
        } else {
            msg.buf = kmalloc(len > I2C_MAX_TRANSFER_SIZE
                              ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
    
            if(!msg.buf)
                return -ENOMEM;
        }
    
        len -= SN65DSI86_ADDR_LENGTH;
    
        while(pos != len) {
            if(unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - SN65DSI86_ADDR_LENGTH))
                transfer_length = I2C_MAX_TRANSFER_SIZE - SN65DSI86_ADDR_LENGTH;
            else
                transfer_length = len - pos;
    
            msg.buf[0] = address;
            msg.len = transfer_length + SN65DSI86_ADDR_LENGTH;
            memcpy(&msg.buf[SN65DSI86_ADDR_LENGTH], &buf[SN65DSI86_ADDR_LENGTH + pos], transfer_length);
    
            for(retry = 0; retry < RETRY_MAX_TIMES; retry++) {
                if(likely(i2c_transfer(client->adapter, &msg, 1) == 1)) {
                    pos += transfer_length;
                    address += transfer_length;
                    break;
                }
    
                dev_info(&client->dev, "I2C write retry[%d]\n", retry + 1);
                udelay(2000);
            }
    
            if(unlikely(retry == RETRY_MAX_TIMES)) {
                dev_err(&client->dev,
                        "I2c write failed,dev:%02x,reg:%02x,size:%u\n",
                        i2c_addr, address, len);
                ret = -EAGAIN;
                goto write_exit;
            }
        }
    
    write_exit:
    
        if(len + SN65DSI86_ADDR_LENGTH >= sizeof(put_buf))
            kfree(msg.buf);
    
        return ret;
    }
    
    /*******************************************************
        Function:
        Read data from the i2c slave device.
        Input:
        client: i2c device.
        buf[0]: read start address.
        buf[1~len-1]: data buffer
        len: SN65DSI86_ADDR_LENGTH + read bytes count
        Output:
        numbers of i2c_msgs to transfer:
            0: succeed, otherwise: failed
     *********************************************************/
    int sn65dsi86_i2c_read(struct i2c_client *client, unsigned short i2c_addr, u8 *buf, int len)
    {
        unsigned int transfer_length = 0;
        unsigned int pos = 0;
        u8 address = buf[0];
        unsigned char get_buf[64], addr_buf[2];
        int retry, ret = 0;
        struct i2c_msg msgs[] = {
            {
                .addr = i2c_addr,
                .flags = !I2C_M_RD,
                .buf = &addr_buf[0],
                .len = SN65DSI86_ADDR_LENGTH,
            }, {
                .addr = i2c_addr,
                .flags = I2C_M_RD,
            }
        };
    
        len -= SN65DSI86_ADDR_LENGTH;
    
        if(likely(len < sizeof(get_buf))) {
            /* code optimize, use stack memory */
            msgs[1].buf = &get_buf[0];
        } else {
            msgs[1].buf = kzalloc(len > I2C_MAX_TRANSFER_SIZE
                                  ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
    
            if(!msgs[1].buf)
                return -ENOMEM;
        }
    
        while(pos != len) {
            if(unlikely(len - pos > I2C_MAX_TRANSFER_SIZE))
                transfer_length = I2C_MAX_TRANSFER_SIZE;
            else
                transfer_length = len - pos;
    
            msgs[0].buf[0] = address;
            msgs[1].len = transfer_length;
    
            for(retry = 0; retry < RETRY_MAX_TIMES; retry++) {
                if(likely(i2c_transfer(client->adapter, msgs, 2) == 2)) {
                    memcpy(&buf[SN65DSI86_ADDR_LENGTH + pos], msgs[1].buf, transfer_length);
                    pos += transfer_length;
                    address += transfer_length;
                    break;
                }
    
                dev_info(&client->dev, "I2c read retry[%d]:0x%x\n",
                         retry + 1, address);
                udelay(2000);
            }
    
            if(unlikely(retry == RETRY_MAX_TIMES)) {
                dev_err(&client->dev,
                        "I2c read failed,dev:%02x,reg:%02x,size:%u\n",
                        i2c_addr, address, len);
                ret = -EAGAIN;
                goto read_exit;
            }
        }
    
    read_exit:
    
        if(len >= sizeof(get_buf))
            kfree(msgs[1].buf);
    
        return ret;
    }
    
    int sn65dsi86_write_1(struct i2c_client *client, u8 addr, u8 data)
    {
        u8 buf[2] = {addr, data};
        int ret = -1;
    
        ret = sn65dsi86_i2c_write(client,client->addr, buf, 2);
    
        return ret;
    }
    
    int sn65dsi86_write(struct i2c_client *client, unsigned short i2c_addr, u8 addr, u8 data)
    {
        u8 buf[2] = {addr, data};
        int ret = -1;
        client->addr = i2c_addr;
        ret = sn65dsi86_i2c_write(client, client->addr,buf, 2);
    
        return ret;
    }
    
    u8 sn65dsi86_read_1(struct i2c_client *client, u8 addr)
    {
        u8 buf[2] = {addr};
        int ret = -1;
    
        ret = sn65dsi86_i2c_read(client,client->addr, buf, 2);
    
        if(ret == 0) {
            return buf[1];
        } else {
            return 0;
        }
    }
    
    u8 sn65dsi86_read(struct i2c_client *client, unsigned short i2c_addr, u8 addr)
    {
        u8 buf[2] = {addr};
        int ret = -1;
        client->addr = i2c_addr;
        ret = sn65dsi86_i2c_read(client,client->addr, buf, 2);
    
        if(ret == 0) {
            return buf[1];
        } else {
            return 0;
        }
    }
    
    int sn65dsi86_power(struct sn65dsi86_data *pdata)
    {
        gpio_direction_output(pdata->rst_gpio, 0);
        gpio_direction_output(pdata->pwr_gpio_33v, 1);
        gpio_set_value(pdata->pwr_gpio_33v, 1);
    	mdelay(150);
    
        gpio_direction_output(pdata->pwr_gpio_12v, 1);
        gpio_set_value(pdata->pwr_gpio_12v, 1);
        mdelay(10);
    
        gpio_direction_output(pdata->pwr_gpio_18v, 1);
        gpio_set_value(pdata->pwr_gpio_18v, 1);
        mdelay(10);
    	
        gpio_direction_output(pdata->rst_gpio, 1);
        mdelay(20);
    	
        return 0;
    }
    
    int sn65dsi86_set_bl(struct sn65dsi86_data *pdata)
    {   
        gpio_direction_output(pdata->bl_gpio, 1);
        gpio_set_value(pdata->bl_gpio, 1);
        mdelay(20);
        gpio_direction_output(pdata->pwr_gpio_dc12v, 1);
        gpio_set_value(pdata->pwr_gpio_dc12v, 1);
    
        return 0;
    }
    
    static int sn65dsi86_parse_dt(struct device *dev,
                                  struct sn65dsi86_data *pdata)
    {
        //int ret = 0;
        struct device_node *np = dev->of_node;
    
        pdata->pwr_gpio_12v = of_get_named_gpio(np, "power-gpio-12v", 0);
    
        if(!gpio_is_valid(pdata->pwr_gpio_12v)) {
            dev_err(dev, "No valid pwr gpio 1.2v");
            return -1;
        }
    
        pdata->pwr_gpio_18v = of_get_named_gpio(np, "power-gpio-18v", 0);
    
        if(!gpio_is_valid(pdata->pwr_gpio_18v)) {
            dev_err(dev, "No valid pwr gpio 1.8v");
            return -1;
        }
    
        pdata->pwr_gpio_33v = of_get_named_gpio(np, "power-gpio-33v", 0);
    
        if(!gpio_is_valid(pdata->pwr_gpio_33v)) {
            dev_err(dev, "No valid pwr gpio 3.3v");
            return -1;
        }
    
        pdata->pwr_gpio_dc12v = of_get_named_gpio(np, "power-gpio-dc12v", 0);
    
        if(!gpio_is_valid(pdata->pwr_gpio_dc12v)) {
            dev_err(dev, "No valid pwr gpio dc12v");
            return -1;
        }
    
        pdata->bl_gpio = of_get_named_gpio(np, "bl-gpio", 0);
    
        if(!gpio_is_valid(pdata->bl_gpio)) {
            dev_err(dev, "No valid bl en gpio");
            return -1;
        }
    
        pdata->rst_gpio = of_get_named_gpio(np, "reset-gpio", 0);
    
        if(!gpio_is_valid(pdata->rst_gpio)) {
            dev_err(dev, "No valid rst gpio");
            return -1;
        }
    
        pdata->pwr_rst_gpio = of_get_named_gpio(np, "power-reset-gpio", 0);
    
        if(!gpio_is_valid(pdata->pwr_rst_gpio)) {
            dev_err(dev, "No valid pwr rst gpio");
            return -1;
        }
        pdata->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0);
    
        //pdata->bl_gpio = of_get_named_gpio(np, "bl-gpio", 0);
    
        return 0;
    }
    static int sn65dsi86_request_io_port(struct sn65dsi86_data *sn65dsi86)
    {
        int ret = 0;
        pr_err("sn65dsi86_request_io_port!!!\n");
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_12v)) {
            ret = gpio_request(sn65dsi86->pwr_gpio_12v, "sn65dsi86_pwr_12v");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->pwr_gpio_12v, ret);
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->pwr_gpio_12v,1);
            gpio_set_value(sn65dsi86->pwr_gpio_12v, 1);
            dev_info(&sn65dsi86->client->dev, "Success request pwr12v-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_18v)) {
            ret = gpio_request(sn65dsi86->pwr_gpio_18v, "sn65dsi86_pwr_18v");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->pwr_gpio_18v, ret);
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->pwr_gpio_18v,1);
            gpio_set_value(sn65dsi86->pwr_gpio_18v, 1);
            dev_info(&sn65dsi86->client->dev, "Success request pwr18v-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_33v)) {
            ret = gpio_request(sn65dsi86->pwr_gpio_33v, "sn65dsi86_pwr_33v");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->pwr_gpio_33v, ret);
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->pwr_gpio_33v,1);
            gpio_set_value(sn65dsi86->pwr_gpio_33v, 1);
            dev_info(&sn65dsi86->client->dev, "Success request pwr33v-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_dc12v)) {
            ret = gpio_request(sn65dsi86->pwr_gpio_dc12v, "sn65dsi86_pwr_dc12v");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->pwr_gpio_dc12v, ret);
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->pwr_gpio_dc12v,1);
            gpio_set_value(sn65dsi86->pwr_gpio_dc12v, 1);
            dev_info(&sn65dsi86->client->dev, "Success request pwr dc 12v-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->rst_gpio)) {
            ret = gpio_request(sn65dsi86->rst_gpio, "sn65dsi86_rst");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->rst_gpio, ret);
    
                if(gpio_is_valid(sn65dsi86->rst_gpio))
                    gpio_free(sn65dsi86->rst_gpio);
    
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->rst_gpio,1);
            gpio_set_value(sn65dsi86->rst_gpio, 1);
            dev_info(&sn65dsi86->client->dev,  "Success request rst-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->pwr_rst_gpio)) {
            ret = gpio_request(sn65dsi86->pwr_rst_gpio, "sn65dsi86_rst1");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->pwr_rst_gpio, ret);
    
                if(gpio_is_valid(sn65dsi86->pwr_rst_gpio))
                    gpio_free(sn65dsi86->pwr_rst_gpio);
    
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->pwr_rst_gpio,0);
            gpio_set_value(sn65dsi86->pwr_rst_gpio, 0);
            dev_info(&sn65dsi86->client->dev,  "Success request rst-gpio\n");
        }
    
        if(gpio_is_valid(sn65dsi86->bl_gpio)) {
            ret = gpio_request(sn65dsi86->bl_gpio, "sn65dsi86_pbl_gpio");
    
            if(ret < 0) {
                dev_err(&sn65dsi86->client->dev,
                        "Failed to request GPIO:%d, ERRNO:%d\n",
                        (s32)sn65dsi86->bl_gpio, ret);
                return -ENODEV;
            }
    
            gpio_direction_output(sn65dsi86->bl_gpio,1);
            gpio_set_value(sn65dsi86->bl_gpio, 1);
            dev_info(&sn65dsi86->client->dev, "Success request bl-gpio\n");
        }
    
        return 0;
    }
    static int sn65dsi86_i2c_test(struct i2c_client *client)
    {
        u8 chip_id_0 = 0, chip_id_1 = 0, chip_id_2 = 0, chip_id_3 = 0, chip_id_4 = 0;
        int ret = -ENODEV;
        pr_err("sn65dsi86_i2c_test!!!\n");
    
        chip_id_0 = sn65dsi86_read(client, EDP_I2C_ADDR, 0x00);//6 ASCII 0x36
        chip_id_1 = sn65dsi86_read(client, EDP_I2C_ADDR, 0x01);//8 ASCII 0x38
        chip_id_2 = sn65dsi86_read(client, EDP_I2C_ADDR, 0x02);//I ASCII 0x49
        chip_id_3 = sn65dsi86_read(client, EDP_I2C_ADDR, 0x03);//S ASCII 0x53
        chip_id_4 = sn65dsi86_read(client, EDP_I2C_ADDR, 0x04);//D ASCII 0x44
        // sn65dsi86 i2c test success chipid: 0x36 0x38 0x49 0x53 0x44
        pr_err("sn65dsi86 i2c test success chipid: 0x%x %x %x %x %x\n",
                chip_id_0, chip_id_1, chip_id_2, chip_id_3, chip_id_4);
        if ((chip_id_1 == 0x38) && (chip_id_0 == 0x36)) {
            pr_err("sn65dsi86_i2c_test success!!!\n");
            ret = 1;
        } else {
            pr_err("sn65dsi86_i2c_test EDP DevID ERROR!!! DevID1=%02X DevID0=%02X\n", chip_id_1, chip_id_0);
        }
    
        return ret;
    }
    void sn65dsi86_init_reg(struct i2c_client *client, struct sn65dsi86_data *pdata)
    {
        sn65dsi86_write(client,EDP_I2C_ADDR,0x09,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x0A,0x04);//dsiclk 09 06 //refclk 02
        sn65dsi86_write(client,EDP_I2C_ADDR,0x0D,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x10,0x26);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x11,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x12,0x00);//refclk 59 5c
        sn65dsi86_write(client,EDP_I2C_ADDR,0x13,0x00);//refclk 59 5c
        sn65dsi86_write(client,EDP_I2C_ADDR,0x20,0x80);//1920
        sn65dsi86_write(client,EDP_I2C_ADDR,0x21,0x07);//1920
        sn65dsi86_write(client,EDP_I2C_ADDR,0x22,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x23,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x24,0x38);//1080
        sn65dsi86_write(client,EDP_I2C_ADDR,0x25,0x04);//1080
        sn65dsi86_write(client,EDP_I2C_ADDR,0x2C,0x14);//hsync-len(hpw)=20
        sn65dsi86_write(client,EDP_I2C_ADDR,0x2D,0x00);//00
        sn65dsi86_write(client,EDP_I2C_ADDR,0x30,0x08);//vsync-len(vpw)=8
        sn65dsi86_write(client,EDP_I2C_ADDR,0x31,0x00);//00
        sn65dsi86_write(client,EDP_I2C_ADDR,0x34,0xA0);//hback-porch(hbp)=160
        sn65dsi86_write(client,EDP_I2C_ADDR,0x36,0x1B);//vback-porch(vbp)=27
        sn65dsi86_write(client,EDP_I2C_ADDR,0x38,0x64);//hfront-porch(hfp)=100
        sn65dsi86_write(client,EDP_I2C_ADDR,0x3A,0x14);//vfront-porch(vfp)=20
        sn65dsi86_write(client,EDP_I2C_ADDR,0x3C,0x00);//pattern model test open:0x10, close:0x00
        sn65dsi86_write(client,EDP_I2C_ADDR,0x3D,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x3E,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x5B,0x00);//00:RGB888, 01:RGB666
        sn65dsi86_write(client,EDP_I2C_ADDR,0x93,0x24);//DP lanes, 2 lanes--->0x24; 4 lanes--->0x34
        sn65dsi86_write(client,EDP_I2C_ADDR,0x94,0x80);//DP data rate, 1.62Gbps--->0x20; 2.7Gbps--->0x80
        sn65dsi86_write(client,EDP_I2C_ADDR,0x5C,0x01);//HPD 0->enable 1->disable
        sn65dsi86_write(client,EDP_I2C_ADDR,0x5A,0x05);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x0d,0x01);
    	mdelay(30);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x64,0x01);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x74,0x00);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x75,0x01);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x76,0x0A);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x77,0x01);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x78,0x81);
    	mdelay(10);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x96,0x01);//01 normal  06 HBR2 mode
        mdelay(10);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x5A,0x0D);	
        mdelay(10);
    	schedule_delayed_work(&pdata->bl_work, msecs_to_jiffies(250));
    }
    
    #ifdef CONFIG_QCOM_DRM
    static int sn65dsi86_check_dt(struct device_node *np)
    {
        int i;
        int count;
        struct device_node *node;
        struct drm_panel *panel;
    
        count = of_count_phandle_with_args(np, "panel", NULL);
        if (count <= 0)
            return 0;
    
        for (i = 0; i < count; i++) {
            node = of_parse_phandle(np, "panel", i);
            panel = of_drm_find_panel(node);
            of_node_put(node);
            if (!IS_ERR(panel)) {
                active_sn65dsi86_panel = panel;
                return 0;
            }
        }
    
        return PTR_ERR(panel);
    }
    
    static void sn65dsi86_resume_work(struct work_struct *work)
    {
    	 struct sn65dsi86_data *sn65dsi86;
    
         sn65dsi86 = container_of(work, struct sn65dsi86_data,
                        resume_work);
    
        sn65dsi86_resume_init(sn65dsi86);
    }
    
    static void sn65dsi86_bl_work(struct work_struct *work)
    {
    	 struct sn65dsi86_data *sn65dsi86;
    	 struct delayed_work *delay_work;
    
    	 delay_work = container_of(work, struct delayed_work, work);
         sn65dsi86 = container_of(delay_work, struct sn65dsi86_data,
                        bl_work);
    	 
    	 sn65dsi86_set_bl(sn65dsi86);
    }
    
    static void sn65dsi86_panel_notifier_callback(enum panel_event_notifier_tag tag,
            struct panel_event_notification *notification, void *client_data)
    {
        struct sn65dsi86_data *sn65dsi86 = client_data;
        //pr_err("tmj sn65dsi86_panel_notifier_callback\n");
    
        if (!notification) {
            pr_err("Invalid notification\n");
            return;
        }
    
        switch (notification->notif_type) {
        case DRM_PANEL_EVENT_UNBLANK:
            if (notification->notif_data.early_trigger)
            {
           		sn65dsi86_power(sn65dsi86);
                //pr_err("resume notification pre commit\n");
            }
            else
                schedule_work(&sn65dsi86->resume_work);
            break;
        case DRM_PANEL_EVENT_BLANK:
            if (notification->notif_data.early_trigger) {
                cancel_work_sync(&sn65dsi86->resume_work);			
    			cancel_delayed_work_sync(&sn65dsi86->bl_work);
                sn65dsi86_vstream_enable_off(sn65dsi86);
            } else {
    			sn65dsi86_suspend_init(sn65dsi86);
                pr_err("suspend notification post commit\n");
            }
            break;
        case DRM_PANEL_EVENT_BLANK_LP:
            pr_info("received lp event\n");
            break;
        case DRM_PANEL_EVENT_FPS_CHANGE:
            pr_info("shashank:Received fps change old fps:%d new fps:%d\n",
                    notification->notif_data.old_fps,
                    notification->notif_data.new_fps);
            break;
        default:
            pr_info("notification serviced :%d\n",
                    notification->notif_type);
            break;
        }
    }
    
    static int sn65dsi86_resume_init(struct sn65dsi86_data *pdata)
    {
    
       // pr_err("tmj sn65dsi86_resume_init\n");
        if(!pdata->is_in_suspend)
            return 0;
    
        if(pdata == NULL) {
            pr_err("sn65dsi86 pdata null !!! \n");
        } else {
                //sn65dsi86_reset(pdata);
                sn65dsi86_init_reg(sn65dsi86_client, pdata);
                pdata->is_in_suspend = FALSE;
        }
        return 0;
    }
    
    static int sn65dsi86_vstream_enable_off(struct sn65dsi86_data *pdata)
    {	
        gpio_direction_output(pdata->bl_gpio, 0);
        mdelay(100);
        gpio_direction_output(pdata->pwr_gpio_dc12v, 0);
        mdelay(100);
        sn65dsi86_write(pSn65dsi86_data->client,EDP_I2C_ADDR,0x5A,0x05);
    	
        return 0;
    }
    
    static int sn65dsi86_suspend_init(struct sn65dsi86_data *pdata)
    {
        pr_err("sn65dsi86_suspend_init enter \n");
    	
    	mdelay(20);
        sn65dsi86_write(pSn65dsi86_data->client,EDP_I2C_ADDR,0x96,0x00);//01 normal  06 HBR2 mode
        sn65dsi86_write(pSn65dsi86_data->client,EDP_I2C_ADDR,0x93,0x00);//DP lanes, 2 lanes--->0x24; 4 lanes--->0x34
        sn65dsi86_write(pSn65dsi86_data->client,EDP_I2C_ADDR,0x0D,0x00);
        gpio_direction_output(pdata->rst_gpio, 0);
    	mdelay(50);
        gpio_direction_output(pdata->pwr_gpio_12v, 0);
        gpio_direction_output(pdata->pwr_gpio_18v, 0);
        gpio_direction_output(pdata->pwr_gpio_33v, 0);
        pdata->is_in_suspend = TRUE;
    	mdelay(100);
        return 0;
    }
    #endif
    
    #ifdef SN65DSI86_NODE
    
    static ssize_t edp_write_register(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
    {
        u8 ret = 0;
    
        pr_err("edp_write_register start\n");
        if(buf){
            pr_err("edp_write_register F0 write 1\n");
            ret = 1;
            sn65dsi86_write(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF0, ret);
        } else {
            pr_err("edp_write_register F0 write 0\n");
            ret = 0;
            sn65dsi86_write(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF0, ret);
        }
    
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF1,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF2,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF3,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF4,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF5,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF6,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF7,buf);
        //sn65dsi86_write(pSn65dsi86_data->client,,EDP_I2C_ADDR,0xF8,buf);
    
        return count;
    
    }
    
    static ssize_t edp_read_register(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
    {
        u8 chip_id_0 = 0, chip_id_1 = 0, chip_id_2 = 0, chip_id_3 = 0,
            chip_id_4 = 0, chip_id_5 = 0, chip_id_6 = 0, chip_id_7 = 0,chip_id_8 = 0;
    
        pr_err("edp_read_register start\n");
    
        chip_id_0 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF0);
        chip_id_1 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF1);
        chip_id_2 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF2);
        chip_id_3 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF3);
        chip_id_4 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF4);
        chip_id_5 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF5);
        chip_id_6 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF6);
        chip_id_7 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF7);
        chip_id_8 = sn65dsi86_read(pSn65dsi86_data->client, EDP_I2C_ADDR, 0xF8);
        pr_err("sn65dsi86 F0~F8 edp_register: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                chip_id_0, chip_id_1, chip_id_2, chip_id_3, chip_id_4,
                chip_id_5, chip_id_6, chip_id_7, chip_id_8);
    
        return scnprintf(buf, PAGE_SIZE, "F0=%x\nF1=%x\nF2=%x\nF3=%x\nF4=%x\nF5=%x\nF6=%x\nF7=%x\nF8=%x\n",
                chip_id_0, chip_id_1, chip_id_2, chip_id_3, chip_id_4, chip_id_5, chip_id_6, chip_id_7, chip_id_8);
    
    }
    
    static struct kobj_attribute dev_attr_edp_info =
        __ATTR(edp_info, 0664, edp_read_register, edp_write_register);
    
    static struct attribute *edp_attrs_ubx[] = {
        &dev_attr_edp_info.attr,
        NULL,
    };
    
    static const struct attribute_group edp_attr_group = {
        .attrs = edp_attrs_ubx,
    };
    #endif
    
    static int sn65dsi86_probe(struct i2c_client * client, const struct i2c_device_id * id)
    {
        int ret = -1;
        struct sn65dsi86_data *sn65dsi86;
    
    #ifdef CONFIG_QCOM_DRM
        void *cookie;
        struct device_node *dt = client->dev.of_node;
        struct drm_panel *active_panel = NULL;
    #endif
        /* do NOT remove these logs */
        pr_err("sn65dsi86 Driver Version: %s\n", SN65DSI86_DRIVER_VERSION);
        pr_err("sn65dsi86 I2C Address: 0x%02x\n", client->addr);
        pr_err("================sn65dsi86_probe==========\n");
        if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
            dev_err(&client->dev, "Failed check I2C functionality");
            return -ENODEV;
        }
        sn65dsi86_client = client;
    
        sn65dsi86 = devm_kzalloc(&client->dev, sizeof(*sn65dsi86), GFP_KERNEL);
    
        if(sn65dsi86 == NULL) {
            dev_err(&client->dev, "Failed alloc sn65dsi86 memory");
            return -ENOMEM;
        }
    
        if(client->dev.of_node) {
            ret = sn65dsi86_parse_dt(&client->dev, sn65dsi86);
    
            if(ret < 0) {
                dev_err(&client->dev, "Failed parse dsn65dsi86\n");
                goto exit_free_client_data;
            }
        }
    
        sn65dsi86->client = client;
        i2c_set_clientdata(client, sn65dsi86);
    
        ret = sn65dsi86_request_io_port(sn65dsi86);
        if(ret < 0) {
            dev_err(&client->dev, "Failed request IO port\n");
            goto exit_free_client_data;
        }
    #ifdef SN65DSI86_DEVID
        ret = sn65dsi86_i2c_test(client);
        if(ret < 0) {
            dev_err(&client->dev, "Failed communicate with IC use I2C\n");
            goto exit_free_io_port;
        }
    
        pSn65dsi86_data = sn65dsi86;
    #endif
    
    #ifdef SN65DSI86_NODE
        edp_kobj = kobject_create_and_add("edp_register", kernel_kobj);
        if (!edp_kobj)
        return -ENOMEM;
    
        ret = sysfs_create_group(edp_kobj, &edp_attr_group);
        if (ret) {
            pr_err("create lcd node failed !!!\n");
            kobject_put(edp_kobj);
        }
    #endif
    
    	INIT_DELAYED_WORK(&sn65dsi86->bl_work, sn65dsi86_bl_work);
    
    #ifdef CONFIG_QCOM_DRM
        ret = sn65dsi86_check_dt(dt);
        if (ret == -EPROBE_DEFER)
        {
            goto exit_free_drm_port;
        }
    
        active_panel = sn65dsi86_get_panel();
    
        INIT_WORK(&sn65dsi86->resume_work, sn65dsi86_resume_work);
        if (active_panel) {
            cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
                    PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_LCD, active_panel,
                    &sn65dsi86_panel_notifier_callback, sn65dsi86);
    
        if (!cookie) {
            dev_err(&client->dev,
                    "%s: Failed to register fb notifier client\n",
                    __func__);
                goto exit_free_drm_port;
        }
            sn65dsi86->notifier_cookie = cookie;
        }
    #endif
        sn65dsi86->is_in_suspend = FALSE;
    
        dev_err(&client->dev, "Sn65dsi86 setup finish.\n");
    
        return 0;
    
    #ifdef CONFIG_QCOM_DRM
    exit_free_drm_port:
        if (active_panel)
            panel_event_notifier_unregister(sn65dsi86->notifier_cookie);
    #endif
    exit_free_io_port:
    	if(gpio_is_valid(sn65dsi86->pwr_gpio_18v))
            gpio_free(sn65dsi86->pwr_gpio_18v);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_33v))
            gpio_free(sn65dsi86->pwr_gpio_33v);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_dc12v))
            gpio_free(sn65dsi86->pwr_gpio_dc12v);
    
        if(gpio_is_valid(sn65dsi86->rst_gpio))
            gpio_free(sn65dsi86->rst_gpio);
    
        if(gpio_is_valid(sn65dsi86->pwr_rst_gpio))
            gpio_free(sn65dsi86->pwr_rst_gpio);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_12v))
            gpio_free(sn65dsi86->pwr_gpio_12v);
    exit_free_client_data:
        devm_kfree(&client->dev, sn65dsi86);
        i2c_set_clientdata(client, NULL);
    
        return ret;
    }
    
    static int sn65dsi86_remove(struct i2c_client * client)
    {
        struct sn65dsi86_data *sn65dsi86 = i2c_get_clientdata(client);
    
    #ifdef CONFIG_QCOM_DRM
        struct drm_panel *active_panel = sn65dsi86_get_panel();
        if (active_panel)
            panel_event_notifier_unregister(sn65dsi86->notifier_cookie);
    #endif
    #ifdef SN65DSI86_NODE 
        kobject_del(edp_kobj);
    #endif
    
        if(gpio_is_valid(sn65dsi86->rst_gpio))
            gpio_free(sn65dsi86->rst_gpio);
    
        if(gpio_is_valid(sn65dsi86->pwr_rst_gpio))
            gpio_free(sn65dsi86->pwr_rst_gpio);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_12v))
            gpio_free(sn65dsi86->pwr_gpio_12v);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_18v))
            gpio_free(sn65dsi86->pwr_gpio_18v);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_33v))
            gpio_free(sn65dsi86->pwr_gpio_33v);
    
        if(gpio_is_valid(sn65dsi86->pwr_gpio_dc12v))
            gpio_free(sn65dsi86->pwr_gpio_dc12v);
    
        i2c_set_clientdata(client, NULL);
    
        devm_kfree(&client->dev, sn65dsi86);
        cancel_work_sync(&sn65dsi86->resume_work);	
    	cancel_delayed_work_sync(&sn65dsi86->bl_work);
        return 0;
    }
    
    
    static const struct of_device_id sn65dsi86_match_table[] = {
        {.compatible = "ti,sn65dsi86_2",},
        { },
    };
    
    static const struct i2c_device_id sn65dsi86_device_id[] = {
        { SN65DSI86_I2C_NAME, 0 },
        { }
    };
    
    static struct i2c_driver sn65dsi86_driver = {
        .probe      = sn65dsi86_probe,
        .remove     = sn65dsi86_remove,
        .id_table   = sn65dsi86_device_id,
        .driver = {
            .name     = SN65DSI86_I2C_NAME,
            .owner    = THIS_MODULE,
            .of_match_table = sn65dsi86_match_table,
        },
    };
    
    static int __init sn65dsi86_init(void)
    {
        s32 ret = 0;
    
        pr_info("sn65dsi86 driver installing....\n");
        if(get_display_panel_type() == 2)
            ret = i2c_add_driver(&sn65dsi86_driver);
        pr_info("sn65dsi86 driver installing end....\n");
        return ret;
    }
    
    static void __exit sn65dsi86_exit(void)
    {
        pr_info("SN65DSI86 driver exited\n");
        i2c_del_driver(&sn65dsi86_driver);
    }
    
    late_initcall(sn65dsi86_init);
    module_exit(sn65dsi86_exit);
    
    MODULE_AUTHOR("Adrien Grassein <adrien.grassein@gmail.com>");
    MODULE_DESCRIPTION("sn65dsi86 drm driver");
    MODULE_LICENSE("GPL v2");
    
    (EDP屏)MG1561B01-3_eDP_Product_SPEC_V0.1_20220308.pdf

  • Hi,

    If not already try using the color bar pattern with the DSI86 to see if the flickering remains.

    The color bar pattern is an internally generated pattern so this will isolate the inputs from the outputs.

    Also please use the DSI86 register calculator if not done. Link: https://e2e.ti.com/support/interface-group/interface/f/interface-forum/1382976/faq-sn65dsi8x-programming-tools?tisearch=e2e-sitesearch&keymatch=sn65dsi86#

    I unfortunately cannot support software as this is outside my realm of expertise. There is a sample driver code found in the link as well.

  • HI: color bar pattern is ok,

  • Hi,

    Can you please map HSYNC and VSYNC to the DSI86 GPIO pins using register 0x5F and measuring HSYNC and VSYNC frequency, do they meet the eDP display requirement?

    Thanks

    David

  • Hi David:

      I wrote 0x5F 0x08 at the end of the initialization, is OK? And then we measure the corresponding HSYNC and VSYNC frequency?

  • Hi,

    Yes, you can write 0x08 to register 0x5F after initialization, and remove R5202, this will let you measure the VSYNC. But you need to lift GPIO3 pin to measure the HSYNC frequency. 

    Thanks

    David

  • Hi,

    I am closing this ticket due to inactivity, you can re-open this ticket by responding to it.

    Thanks

    David