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: Question about SN65DSI86

Part Number: SN65DSI86

Tool/software:

Hello:

Problem phenomenon: After deep sleep of the EDP screen, there is a probability that it cannot be awakened

Preliminary analysis:

Print the register values from F0 to F8 and see what the reason is. After comparing the registers, it was found that F0 is 1 when the screen is normally lit, and 0 when the screen is abnormally black. All other registers are the same without any difference,

So we need to see what causes the register difference?

The attachment contains relevant driver code and dtsi configuration

 

sn65dsi86.c
/*
    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 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_reset(struct sn65dsi86_data *pdata)
{
    pr_err("sn65dsi86_reset\n");

    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(5);
    gpio_direction_output(pdata->rst_gpio, 0);
    mdelay(20);
    gpio_direction_output(pdata->rst_gpio, 1);

    mdelay(20);
    gpio_direction_output(pdata->bl_gpio, 1);
    gpio_set_value(pdata->bl_gpio, 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->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->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->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->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)
{
    pr_err("tmj sn65dsi86_init_reg start!!!\n");

    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);

    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);
    sn65dsi86_write(client,EDP_I2C_ADDR,0x96,0x01);//01 normal  06 HBR2 mode
    sn65dsi86_write(client,EDP_I2C_ADDR,0x5A,0x0D);

    pr_err("tmj sn65dsi86_init_reg end!!!\n");
}

#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 = container_of(work, struct sn65dsi86_data,
                    resume_work);

    sn65dsi86_resume_init(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;
    }

    pr_info("Notification type:%d, early_trigger:%d",
            notification->notif_type,
            notification->notif_data.early_trigger);
    switch (notification->notif_type) {
    case DRM_PANEL_EVENT_UNBLANK:
        if (notification->notif_data.early_trigger)
            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);
            sn65dsi86_suspend_init(sn65dsi86);
        } else {
            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->is_in_suspend = FALSE;
    }
    return 0;
}

static int sn65dsi86_suspend_init(struct sn65dsi86_data *pdata)
{
    pr_err("sn65dsi86_suspend_init enter \n");

    gpio_direction_output(pdata->bl_gpio, 0);
    gpio_direction_output(pdata->pwr_gpio_12v, 0);
    gpio_direction_output(pdata->pwr_gpio_18v, 0);
    gpio_direction_output(pdata->rst_gpio, 0);
    pdata->is_in_suspend = TRUE;
    return 0;
}
#endif

#ifdef SN65DSI86_NODE
static ssize_t edp_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_register info\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_register, NULL);

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


#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_TOUCH, 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->rst_gpio))
        gpio_free(sn65dsi86->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_gpio_12v))
        gpio_free(sn65dsi86->pwr_gpio_12v);

    if(gpio_is_valid(sn65dsi86->pwr_gpio_18v))
        gpio_free(sn65dsi86->pwr_gpio_18v);

    i2c_set_clientdata(client, NULL);

    devm_kfree(&client->dev, sn65dsi86);
    cancel_work_sync(&sn65dsi86->resume_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");
 sn65dsi86.h

normal:

abnormal black screen

  • Hi,

    How are you putting the eDP screen into the deep sleep mode and are you also putting the DSI86 into deep sleep mode as well? 

    Thanks

    David

  • Hi David:

    1. The customer directly entered deep sleep mode in the system.

    2. How to confirm if Dsi86 has entered deep sleep?

  • Hi,

    Are they implementing the Suspend Mode with DSI86? Please see section 8.3.3.2 Suspend Mode for more detail. 

    I need to know more about the steps entering and exiting the deep sleep mode in order to further help debugging this issue.

    Thanks

    David

  • Hi David:

    The attachment is the code currently being used by the customer. Could you please help check if it has entered suspend mode? Thank you for your guidance!!!

    5228.sn65dsi86.c
    /*
        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 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_reset(struct sn65dsi86_data *pdata)
    {
        pr_err("sn65dsi86_reset\n");
    
        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->pwr_gpio_33v, 1);
        gpio_set_value(pdata->pwr_gpio_33v, 1);
        mdelay(10);
    
        gpio_direction_output(pdata->rst_gpio, 1);
        mdelay(5);
        gpio_direction_output(pdata->rst_gpio, 0);
        mdelay(20);
        gpio_direction_output(pdata->rst_gpio, 1);
    
        mdelay(20);
        gpio_direction_output(pdata->bl_gpio, 1);
        gpio_set_value(pdata->bl_gpio, 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->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->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->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->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)
    {
        pr_err("tmj sn65dsi86_init_reg start!!!\n");
    
        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);
    
        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);
        sn65dsi86_write(client,EDP_I2C_ADDR,0x96,0x01);//01 normal  06 HBR2 mode
        sn65dsi86_write(client,EDP_I2C_ADDR,0x5A,0x0D);
    
        pr_err("tmj sn65dsi86_init_reg end!!!\n");
    }
    
    #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 = container_of(work, struct sn65dsi86_data,
                        resume_work);
    
        sn65dsi86_resume_init(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;
        }
    
        pr_info("Notification type:%d, early_trigger:%d",
                notification->notif_type,
                notification->notif_data.early_trigger);
        switch (notification->notif_type) {
        case DRM_PANEL_EVENT_UNBLANK:
            if (notification->notif_data.early_trigger)
                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);
                sn65dsi86_suspend_init(sn65dsi86);
            } else {
                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->is_in_suspend = FALSE;
        }
        return 0;
    }
    
    static int sn65dsi86_suspend_init(struct sn65dsi86_data *pdata)
    {
        pr_err("sn65dsi86_suspend_init enter \n");
    
        gpio_direction_output(pdata->bl_gpio, 0);
        gpio_direction_output(pdata->pwr_gpio_12v, 0);
        gpio_direction_output(pdata->pwr_gpio_18v, 0);
        gpio_direction_output(pdata->pwr_gpio_33v, 0);
        gpio_direction_output(pdata->rst_gpio, 0);
        pdata->is_in_suspend = TRUE;
        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
    
    
    #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_TOUCH, 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->rst_gpio))
            gpio_free(sn65dsi86->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_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);
    
        i2c_set_clientdata(client, NULL);
    
        devm_kfree(&client->dev, sn65dsi86);
        cancel_work_sync(&sn65dsi86->resume_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");
    

  • Hey Jimmy,

    We unfortunately do not support software. Regarding this deep sleep mode. What are the input and output signal status while in deep sleep mode?