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
/* 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");
normal:
abnormal black screen