高通SDM450 副屏mipi转EDP

高通SDM450 副屏mipi转EDP的调试基础是在之前主屏mipi转EDP的基础上面实现的,调试难度比主屏mipi转EDP简单一些,本来是也是在BootLoader的阶段对lt8911进行初始化,但是SDM450 BootLoader阶段的I2C2初始化一直有问题,可能是域错了,厂家也没给具体的实现,后来就放弃了在BootLoader阶段初始化lt8911,毕竟副屏是要进入Android才有图像输出,因而选择在kernel阶段初始化lt8911.本次调试副屏mipi转EDP,lt8911exb 的I2C接到SDM450的I2C2接口上,其他硬件接口跟主屏mipi转EDP差不多,只是一些控制上电和复位的管脚不一样,具体的硬件电路不再贴出来,可以参考之前的主屏mipi转EDP的电路 

主屏mipi转EDP 调试过程 原文链接:https://blog.csdn.net/u010852497/article/details/115266158
调试过程简要:

1、LT8911硬件部分各个电源电压是否正常,晶振是否起振,I2C电平状态是否正常

2、添加LT8911设备,注意包括电源、复位的控制IO口,挂载在LT8911上屏幕是多少lane的

3、添加LT8911驱动,确保通信正常,可以出彩条

4、添加SDM450副屏部分参数,注意主控跟LT8911连接是几lane的

 

一、LT8911硬件部分各个电源电压是否正常,晶振是否起振,I2C电平状态是否正常

这部分的调试使用示波器来测量,一定要确保LT8911能够正常工作,由于调试过程没保存好图片,这里不贴出来具体测量过程

 

二、添加LT8911设备

 kernel4.9来说,设备的描述采用dts设备树描述,我们副屏的LT8911是接到SDM450的I2C2,所有可以要将LT8911挂到I2C2上面

kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi

&i2c_2 {

    lt8911exb@29 {
        compatible = "lontium,lt8911exb";
        reg = <0x29>;
        power-gpio = <&tlmm 25 0x0>;//LCD0_EN
        reset-gpio = <&tlmm 87 0x0>;//LT8912_RSTN 
        //bl-gpio = <&tlmm 44 0x0>;//LCD0_BL_EN

        lontium,pclk = <73248000>;//<148000000>;
        lontium,hfp = <94>;
        lontium,hs = <16>;    
        lontium,hbp = <50>;                
        lontium,hact = <1366>;        
        lontium,vfp = <13>;        
        lontium,vs = <6>;    
        lontium,vbp = <13>;            
        lontium,vact = <768>;    

        lontium,mipi_lane = <4>;
        lontium,lane_cnt = <1>;
        lontium,color = <1>; //1//Color Depth 0:6bit 1:8bit
        lontium,test = <0>;
    };

注意事项:

1、本次调试的地址是0x29,具体的要根据LT8911的硬件决定

2、pclk = (hfp+hs+hbo+hact)*(vfp+vs+vbp+vact)*60  这个具体也是要看驱动的解析

3、mipi_lane  主控和lt8911连接的lane数

4、lane_cnt   lt8911和屏幕连接的lane数

5、test 是否开启彩条测试模式

 

三、添加LT8911的驱动

kernel/msm-4.9/drivers/video/lt8911exb/lt8911exb.c

#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/input/mt.h>
#include <linux/slab.h>
#include "lt8911exb.h"

/*******************************************************
    Function:
    Write data to the i2c slave device.
    Input:
    client: i2c device.
    buf[0]: write start address.
    buf[1~len-1]: data buffer
    len: LT8911EXB_ADDR_LENGTH + write bytes count
    Output:
    numbers of i2c_msgs to transfer:
        0: succeed, otherwise: failed
 *********************************************************/
int lt8911exb_i2c_write(struct i2c_client *client, 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 = client->addr,
        .flags = !I2C_M_RD,
    };

    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 -= LT8911EXB_ADDR_LENGTH;
    while(pos != len) {
        if(unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - LT8911EXB_ADDR_LENGTH))
            transfer_length = I2C_MAX_TRANSFER_SIZE - LT8911EXB_ADDR_LENGTH;
        else
            transfer_length = len - pos;
        msg.buf[0] = address;
        msg.len = transfer_length + LT8911EXB_ADDR_LENGTH;
        memcpy(&msg.buf[LT8911EXB_ADDR_LENGTH], &buf[LT8911EXB_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",
                    client->addr, address, len);
            ret = -EAGAIN;
            goto write_exit;
        }
    }
write_exit:

    if(len + LT8911EXB_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: LT8911EXB_ADDR_LENGTH + read bytes count
    Output:
    numbers of i2c_msgs to transfer:
        0: succeed, otherwise: failed
 *********************************************************/
int lt8911exb_i2c_read(struct i2c_client *client, 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 = client->addr,
            .flags = !I2C_M_RD,
            .buf = &addr_buf[0],
            .len = LT8911EXB_ADDR_LENGTH,
        }, {
            .addr = client->addr,
            .flags = I2C_M_RD,
        }
    };
    len -= LT8911EXB_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[LT8911EXB_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",
                    client->addr, address, len);
            ret = -EAGAIN;
            goto read_exit;
        }
    }

read_exit:

    if(len >= sizeof(get_buf))
        kfree(msgs[1].buf);

    return ret;
}

int lt8911exb_write(struct i2c_client *client, u8 addr, u8 data)
{
    u8 buf[2] = {addr, data};
    int ret = -1;

    ret = lt8911exb_i2c_write(client, buf, 2);

    return ret;
}

u8 lt8911exb_read(struct i2c_client *client, u8 addr)
{
    u8 buf[2] = {addr};
    int ret = -1;

    ret = lt8911exb_i2c_read(client, buf, 2);

    if(ret == 0) {
        return buf[1];
    } else {
        return 0;
    }
}

void lt8911exb_reset_guitar(struct i2c_client *client)
{
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);

    dev_info(&client->dev, "Guitar reset");
    if(!gpio_is_valid(lt8911exb->rst_gpio)) {
        dev_warn(&client->dev, "reset failed no valid reset gpio");
        return;
    }
	gpio_direction_output(lt8911exb->rst_gpio, 1);
	mdelay(100); //150ms
    gpio_direction_output(lt8911exb->rst_gpio, 0);
	mdelay(100);
	gpio_direction_output(lt8911exb->rst_gpio, 1);
    mdelay(10);
	//gpio_direction_output(lt8911exb->bl_gpio, 1);
	
}

static int lt8911exb_parse_dt(struct device *dev,
                              struct lt8911exb_data *pdata)
{
    int ret = 0;
    struct device_node *np = dev->of_node;

    pdata->pwr_gpio = of_get_named_gpio(np, "power-gpio", 0);

    if(!gpio_is_valid(pdata->pwr_gpio)) {
        dev_err(dev, "No valid pwr 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->bl_gpio = of_get_named_gpio(np, "bl-gpio", 0);

    if(!gpio_is_valid(pdata->rst_gpio)) {
        dev_err(dev, "No valid bl gpio");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,test", &pdata->test);
    if (ret) {
        dev_err(dev, "Parse test failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,mipi_lane", &pdata->mipi_lane);
    if (ret) {
        dev_err(dev, "Parse mipi_lane failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,lane_cnt", &pdata->lane_cnt);
    if (ret) {
        dev_err(dev, "Parse lane_cnt failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,color", &pdata->color);
    if (ret) {
        dev_err(dev, "Parse color failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,lane_cnt", &pdata->lane_cnt);
    if (ret) {
        dev_err(dev, "Parse lane_cnt failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,hact", &pdata->hact);
    if (ret) {
        dev_err(dev, "Parse hact failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,hact", &pdata->hact);
    if (ret) {
        dev_err(dev, "Parse hact failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,vact", &pdata->vact);
    if (ret) {
        dev_err(dev, "Parse vact failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,hbp", &pdata->hbp);
    if (ret) {
        dev_err(dev, "Parse hbp failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,hfp", &pdata->hfp);
    if (ret) {
        dev_err(dev, "Parse hfp failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,hs", &pdata->hs);
    if (ret) {
        dev_err(dev, "Parse hs failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,vbp", &pdata->vbp);
    if (ret) {
        dev_err(dev, "Parse vbp failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,vfp", &pdata->vfp);
    if (ret) {
        dev_err(dev, "Parse vfp failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,vs", &pdata->vs);
    if (ret) {
        dev_err(dev, "Parse vs failed");
        return -1;
    }

    ret = of_property_read_u32(np, "lontium,pclk", &pdata->pclk);
    if (ret) {
        dev_err(dev, "Parse pclk failed");
        return -1;
    } else {
        pdata->pclk = pdata->pclk/10000; // 10khz
    }

    pdata->htotal = pdata->hact + pdata->hbp + pdata->hfp + pdata->hs;
    pdata->vtotal = pdata->vact + pdata->vbp + pdata->vfp + pdata->vs;

    return 0;
}


static int lt8911exb_request_io_port(struct lt8911exb_data *lt8911exb)
{
    int ret = 0;

    if(gpio_is_valid(lt8911exb->pwr_gpio)) {
        ret = gpio_request(lt8911exb->pwr_gpio, "lt8911exb_pwr");

        if(ret < 0) {
            dev_err(&lt8911exb->client->dev,
                    "Failed to request GPIO:%d, ERRNO:%d\n",
                    (s32)lt8911exb->pwr_gpio, ret);
            return -ENODEV;
        }
        gpio_direction_output(lt8911exb->pwr_gpio , 1);
        dev_info(&lt8911exb->client->dev, "Success request pwr-gpio\n");
    }

    if(gpio_is_valid(lt8911exb->rst_gpio)) {
        ret = gpio_request(lt8911exb->rst_gpio, "lt8911exb_rst");

        if(ret < 0) {
            dev_err(&lt8911exb->client->dev,
                    "Failed to request GPIO:%d, ERRNO:%d\n",
                    (s32)lt8911exb->rst_gpio, ret);

            if(gpio_is_valid(lt8911exb->pwr_gpio))
                gpio_free(lt8911exb->pwr_gpio);


            return -ENODEV;
        }
        //gpio_direction_input(lt8911exb->rst_gpio);
        dev_info(&lt8911exb->client->dev,  "Success request rst-gpio\n");
    }

	if(gpio_is_valid(lt8911exb->bl_gpio)) {
        ret = gpio_request(lt8911exb->bl_gpio, "lt8911exb_bl");

        if(ret < 0) {
            dev_err(&lt8911exb->client->dev,
                    "Failed to request GPIO:%d, ERRNO:%d\n",
                    (s32)lt8911exb->bl_gpio, ret);
			if(gpio_is_valid(lt8911exb->pwr_gpio))
                gpio_free(lt8911exb->pwr_gpio);
			
            if(gpio_is_valid(lt8911exb->rst_gpio))
                gpio_free(lt8911exb->rst_gpio);

            return -ENODEV;
        }

        dev_info(&lt8911exb->client->dev,  "Success request bl-gpio\n");
    }
	
    return 0;
}

static int lt8911exb_i2c_test(struct i2c_client *client)
{
    u8 retry = 0;
    u8 chip_id_h = 0, chip_id_m = 0, chip_id_l = 0;
    int ret = -EAGAIN;

    while(retry++ < 3) {
        ret = lt8911exb_write(client, 0xff, 0x81);

        if(ret < 0) {
            dev_err(&client->dev, "LT8911EXB i2c test write addr:0xff failed\n");
            continue;
        }
        ret = lt8911exb_write(client, 0x08, 0x7f);

        if(ret < 0) {
            dev_err(&client->dev, "LT8911EXB i2c test write addr:0x08 failed\n");
            continue;
        }
        chip_id_l = lt8911exb_read(client, 0x00);
        chip_id_m = lt8911exb_read(client, 0x01);
        chip_id_h = lt8911exb_read(client, 0x02);
        // LT8911EXB i2c test success chipid: 0xe0517
        dev_info(&client->dev, "LT8911EXB i2c test success chipid: 0x%x%x%x\n", chip_id_h, chip_id_m, chip_id_l);

//        if (chip_id_h == 0 && chip_id_l == 0) {
//            dev_err(&client->dev, "LT8911EXB i2c test failed time %d\n", retry);
//            continue;
//        }
        ret = 0;
        break;
    }

    return ret;
}

void lt8911exb_dpcd_write(struct i2c_client *client, u32 address, u8 Data)
{
    u8 address_h = 0x0f & (address >> 16);
    u8 address_m = 0xff & (address >> 8);
    u8 address_l = 0xff & address;
    u8 ret = 0x00;

    lt8911exb_write(client, 0xff, 0xa6);
    lt8911exb_write(client, 0x2b, (0x80|address_h));
    lt8911exb_write(client, 0x2b,address_m); //addr[15:8]
    lt8911exb_write(client, 0x2b,address_l); //addr[7:0]
    lt8911exb_write(client, 0x2b,0x00); //data lenth
    lt8911exb_write(client, 0x2b,Data); //data
    lt8911exb_write(client, 0x2c,0x00); //start Aux read edid

    mdelay(20);
    ret = lt8911exb_read(client, 0x25);
    if((ret&0x0f)== 0x0c) {
        return;
    }
}

u8 lt8911exb_dpcd_read(struct i2c_client *client, u32 address)
{
    u8 read_cnt = 0x03;
    u8 dpcd_value = 0x00;
    u8 address_h = 0x0f & (address >> 16);
    u8 address_m = 0xff & (address >> 8);
    u8 address_l = 0xff & address;

    lt8911exb_write(client, 0xff, 0x80);
    lt8911exb_write(client, 0x62, 0xbd);
    lt8911exb_write(client, 0x62, 0xbf); //ECO(AUX reset)

    lt8911exb_write(client, 0x36, 0x00);
    lt8911exb_write(client, 0x30, 0x8f); //0x91
    lt8911exb_write(client, 0x33, address_l);
    lt8911exb_write(client, 0x34, address_m);
    lt8911exb_write(client, 0x35, address_h);
    lt8911exb_write(client, 0x36, 0x20);

    mdelay(2); //The necessary

    if(lt8911exb_read(client, 0x39) == 0x01) {
        dpcd_value = lt8911exb_read(client, 0x38);
    } else {
        while((lt8911exb_read(client, 0x39) != 0x01) && (read_cnt > 0)) {
            lt8911exb_write(client, 0x36, 0x00);
            lt8911exb_write(client, 0x36, 0x20);
            read_cnt--;
            mdelay(2);
        }

        if(lt8911exb_read(client, 0x39) == 0x01) {
            dpcd_value = lt8911exb_read(client, 0x38);
        }
    }

    return dpcd_value;
}

void lt8911exb_mipi_video_timing(struct i2c_client *client)
{
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);
    
    lt8911exb_write(client, 0xff, 0xd0);
    lt8911exb_write(client, 0x0d, (u8)(lt8911exb->vtotal / 256));
    lt8911exb_write(client, 0x0e, (u8)(lt8911exb->vtotal % 256));    //vtotal
    lt8911exb_write(client, 0x0f, (u8)(lt8911exb->vact / 256));
    lt8911exb_write(client, 0x10, (u8)(lt8911exb->vact % 256));      //vactive
    lt8911exb_write(client, 0x11, (u8)(lt8911exb->htotal / 256));
    lt8911exb_write(client, 0x12, (u8)(lt8911exb->htotal % 256));    //htotal
    lt8911exb_write(client, 0x13, (u8)(lt8911exb->hact / 256));
    lt8911exb_write(client, 0x14, (u8)(lt8911exb->hact % 256));      //hactive
    lt8911exb_write(client, 0x15, (u8)(lt8911exb->vs % 256));        //vsa
    lt8911exb_write(client, 0x16, (u8)(lt8911exb->hs % 256));        //hsa
    lt8911exb_write(client, 0x17, (u8)(lt8911exb->vfp / 256));
    lt8911exb_write(client, 0x18, (u8)(lt8911exb->vfp % 256));       //vfp
    lt8911exb_write(client, 0x19, (u8)(lt8911exb->hfp / 256));
    lt8911exb_write(client, 0x1a, (u8)(lt8911exb->hfp % 256));       //hfp
}

void lt8911exb_edp_video_cfg(struct i2c_client *client)
{
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);
    
    lt8911exb_write(client, 0xff, 0xa8);
    lt8911exb_write(client, 0x2d, 0x88); // MSA from register
    lt8911exb_write(client, 0x05, (u8)(lt8911exb->htotal / 256));
    lt8911exb_write(client, 0x06, (u8)(lt8911exb->htotal % 256));
    lt8911exb_write(client, 0x07, (u8)((lt8911exb->hs + lt8911exb->hbp) / 256));
    lt8911exb_write(client, 0x08, (u8)((lt8911exb->hs + lt8911exb->hbp) % 256));
    lt8911exb_write(client, 0x09, (u8)(lt8911exb->hs / 256));
    lt8911exb_write(client, 0x0a, (u8)(lt8911exb->hs % 256));
    lt8911exb_write(client, 0x0b, (u8)(lt8911exb->hact / 256));
    lt8911exb_write(client, 0x0c, (u8)(lt8911exb->hact % 256));
    lt8911exb_write(client, 0x0d, (u8)(lt8911exb->vtotal / 256));
    lt8911exb_write(client, 0x0e, (u8)(lt8911exb->vtotal % 256));
    lt8911exb_write(client, 0x11, (u8)((lt8911exb->vs + lt8911exb->vbp) / 256));
    lt8911exb_write(client, 0x12, (u8)((lt8911exb->vs + lt8911exb->vbp) % 256));
    lt8911exb_write(client, 0x14, (u8)(lt8911exb->vs % 256));
    lt8911exb_write(client, 0x15, (u8)(lt8911exb->vact / 256));
    lt8911exb_write(client, 0x16, (u8)(lt8911exb->vact % 256));
}

void lt8911exb_setup(struct i2c_client *client)
{
    u8 i;
    u8 pcr_pll_postdiv;
    u8 pcr_m;
    u16 temp16;
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);


    /* init */
    lt8911exb_write(client, 0xff, 0x81);
    lt8911exb_write(client, 0x49, 0xff); //enable 0x87xx
    lt8911exb_write(client, 0xff, 0x82); //GPIO test output
    lt8911exb_write(client, 0x5a, 0x0e);

    /* mipi Rx analog */
    lt8911exb_write(client, 0xff, 0x82);
    lt8911exb_write(client, 0x32, 0x51);
    lt8911exb_write(client, 0x35, 0x42); //EQ current
    lt8911exb_write(client, 0x4c, 0x0c);
    lt8911exb_write(client, 0x4d, 0x00);
    lt8911exb_write(client, 0x3a, 0x44); // 11 // 22 / 33 / 55 / 66 / 77
    lt8911exb_write(client, 0x3b, 0x44); // 11 // 22 / 33 / 55 / 66 / 77

    /* dessc_pcr  pll analog */
    lt8911exb_write(client, 0xff, 0x82);
    lt8911exb_write(client, 0x6a, 0x40);
    lt8911exb_write(client, 0x6b, 0x40);

    temp16 = lt8911exb->pclk;

    if(lt8911exb->pclk < 8800) {
        lt8911exb_write(client, 0x6e, 0x82); //0x44:pre-div = 2 ,pixel_clk=44~ 88MHz
        pcr_pll_postdiv = 0x08;
    } else {
        lt8911exb_write(client, 0x6e, 0x81); //0x40:pre-div = 1, pixel_clk =88~176MHz
        pcr_pll_postdiv = 0x04;
    }

    pcr_m = (u8)(temp16 * pcr_pll_postdiv / 25 / 100);

    /* dessc pll digital */
    lt8911exb_write(client, 0xff, 0x85);
    lt8911exb_write(client, 0xa9, 0x31);
    lt8911exb_write(client, 0xaa, 0x17);
    lt8911exb_write(client, 0xab, 0xba);
    lt8911exb_write(client, 0xac, 0xe1);
    lt8911exb_write(client, 0xad, 0x47);
    lt8911exb_write(client, 0xae, 0x01);
    lt8911exb_write(client, 0xae, 0x11);

    /* Digital Top */
    lt8911exb_write(client, 0xff, 0x85);
    lt8911exb_write(client, 0xc0, 0x01); //select mipi Rx
#ifdef _6bit_
    lt8911exb_write(client, 0xb0, 0xd0); //enable dither
#else
    lt8911exb_write(client, 0xb0, 0x00); // disable dither
#endif

    /* mipi Rx Digital */
    lt8911exb_write(client, 0xff, 0xd0);
    lt8911exb_write(client, 0x00, lt8911exb->mipi_lane % 4); // 0: 4 Lane / 1: 1 Lane / 2 : 2 Lane / 3: 3 Lane
    lt8911exb_write(client, 0x02, 0x08); //settle
    lt8911exb_write(client, 0x08, 0x00);
    lt8911exb_write(client, 0x0a, 0x12); //pcr mode
    lt8911exb_write(client, 0x0c, 0x40);

    lt8911exb_write(client, 0x1c, 0x3a);
    //lt8911exb_write(client,0x2d,0x19); //M up limit
    lt8911exb_write(client, 0x31, 0x0a);
//  lt8911exb_write(client, 0x21, 0x4f );
//  lt8911exb_write(client, 0x22, 0xff );
//  lt8911exb_write(client, 0x2a, 0x08 );

    lt8911exb_write(client, 0x3f, 0x10);
    lt8911exb_write(client, 0x40, 0x20);
    lt8911exb_write(client, 0x41, 0x30);

    if (lt8911exb->test) {
		dev_info(&client->dev, "LT8911EXB i2c test \n");
        lt8911exb_write(client, 0x26, (pcr_m | 0x80));
    } else {
        lt8911exb_write(client, 0x26, pcr_m);
    }

    lt8911exb_write(client, 0xff, 0x81); //PCR reset
    lt8911exb_write(client, 0x03, 0x7b);
    lt8911exb_write(client, 0x03, 0xff);

    /* Txpll 2.7G*/
    lt8911exb_write(client, 0xff, 0x87);
    lt8911exb_write(client, 0x19, 0x31);
    lt8911exb_write(client, 0xff, 0x82);
    lt8911exb_write(client, 0x02, 0x42);
    lt8911exb_write(client, 0x03, 0x00);
    lt8911exb_write(client, 0x03, 0x01);
    lt8911exb_write(client, 0xff, 0x81);
    lt8911exb_write(client, 0x09, 0xfc);
    lt8911exb_write(client, 0x09, 0xfd);
    lt8911exb_write(client, 0xff, 0x87);
    lt8911exb_write(client, 0x0c, 0x11);

    for(i = 0; i < 5; i++) { //Check Tx PLL
        mdelay(5);
        if(lt8911exb_read(client, 0x37) & 0x02) {
            dev_info(&client->dev,  "LT8911 tx pll locked");
            break;
        } else {
            dev_info(&client->dev,  "LT8911 tx pll unlocked");
            lt8911exb_write(client, 0xff, 0x81);
            lt8911exb_write(client, 0x09, 0xfc);
            lt8911exb_write(client, 0x09, 0xfd);
            lt8911exb_write(client, 0xff, 0x87);
            lt8911exb_write(client, 0x0c, 0x10);
            lt8911exb_write(client, 0x0c, 0x11);
        }
    }

    /* tx phy */
    lt8911exb_write(client, 0xff, 0x82);
    lt8911exb_write(client, 0x11, 0x00);
    lt8911exb_write(client, 0x13, 0x10);
    lt8911exb_write(client, 0x14, 0x0c);
    lt8911exb_write(client, 0x14, 0x08);
    lt8911exb_write(client, 0x13, 0x20);
    lt8911exb_write(client, 0xff, 0x82);
    lt8911exb_write(client, 0x0e, 0x25);
    lt8911exb_write(client, 0x12, 0xff);
//  lt8911exb_write(client, 0xff, 0x80 );
//  lt8911exb_write(client, 0x40, 0x22 );

    /*eDP Tx Digital */
    lt8911exb_write(client, 0xff, 0xa8);
    if (lt8911exb->test) {
        lt8911exb_write(client, 0x24, 0x50); // bit2 ~ bit 0 : test panttern image mode
        lt8911exb_write(client, 0x25, 0x70); // bit6 ~ bit 4 : test Pattern color
        lt8911exb_write(client, 0x27, 0x50); //0x50:Pattern; 0x10:mipi video
    } else {
        lt8911exb_write(client, 0x27, 0x10); //0x50:Pattern; 0x10:mipi video
    }

    if(lt8911exb->color) {
        //8bit
        lt8911exb_write(client, 0x17, 0x10);
        lt8911exb_write(client, 0x18, 0x20);
    } else {
        //6bit
        lt8911exb_write(client, 0x17, 0x00);
        lt8911exb_write(client, 0x18, 0x00);
    }
    
    lt8911exb_write(client, 0xff, 0xa0);
    lt8911exb_write(client, 0x00, 0x08);
    lt8911exb_write(client, 0x01, 0x00);
}

/* mipi should be ready before configuring below video check setting*/
void lt8911exb_video_check(struct i2c_client *client)
{
    u32 ret = 0x00;

    /* mipi byte clk check*/
    lt8911exb_write(client, 0xff, 0x85);
    lt8911exb_write(client, 0x1d, 0x00); //FM select byte clk
    lt8911exb_write(client, 0x40, 0xf7);
    lt8911exb_write(client, 0x41, 0x30);
    lt8911exb_write(client, 0xa1, 0x82); //eDP scramble mode; //video chech from mipi

//  lt8911exb_write(client, 0x17, 0xf0 ); //0xf0:Close scramble; 0xD0 : Open scramble

    lt8911exb_write(client, 0xff, 0x81); //video check rst
    lt8911exb_write(client, 0x09, 0x7d);
    lt8911exb_write(client, 0x09, 0xfd);
    lt8911exb_write(client, 0xff, 0x85);
    mdelay(30);

    if(lt8911exb_read(client, 0x50) == 0x03) {
        ret = lt8911exb_read(client, 0x4d);
        ret = ret * 256 + lt8911exb_read(client, 0x4e);
        ret = ret * 256 + lt8911exb_read(client, 0x4f);

        dev_info(&client->dev,  "video check: mipi clk = %d", ret); //mipi clk = ret * 1000
    } else {
        dev_info(&client->dev,  "video check: mipi clk unstable");
    }

    /* mipi vtotal check*/
    ret    = lt8911exb_read(client, 0x76);
    ret    = ret * 256 + lt8911exb_read(client, 0x77);
    dev_info(&client->dev,  "video check: Vtotal = %d", ret);

    /* mipi word count check*/
    lt8911exb_write(client, 0xff, 0xd0);
    ret = lt8911exb_read(client, 0x82);
    ret = ret * 256 + lt8911exb_read(client, 0x83);
    ret = ret / 3;

    dev_info(&client->dev,  "video check: Hact(word counter) = %d", ret);

    /* mipi Vact check*/
    ret    = lt8911exb_read(client, 0x85);
    ret    = ret * 256 + lt8911exb_read(client, 0x86);

    dev_info(&client->dev,  "video check: Vact = %d", ret);
}

void lt8911exb_link_train(struct i2c_client *client)
{
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);
    
    lt8911exb_write(client, 0xff, 0x85);
    lt8911exb_write(client, 0xa1, 0x82);   // eDP scramble mode;

    /* Aux operater init */
    lt8911exb_write(client,0xff,0xac);
    lt8911exb_write(client,0x00,0x20);  //Soft Link train
    lt8911exb_write(client,0xff,0xa6);
    lt8911exb_write(client,0x2a,0x01);

    lt8911exb_dpcd_write(client, 0x010a, 0x01);
    mdelay(10);
    lt8911exb_dpcd_write(client, 0x0102, 0x00);
    mdelay(10);
    lt8911exb_dpcd_write(client, 0x010a, 0x01);

    mdelay(200);
    /* Aux setup */
    lt8911exb_write(client, 0xff, 0xac);
    lt8911exb_write(client, 0x00, 0x60);  //Soft Link train
    lt8911exb_write(client, 0xff, 0xa6);
    lt8911exb_write(client, 0x2a, 0x00);
    lt8911exb_write(client, 0xff, 0x81);
    lt8911exb_write(client, 0x07, 0xfe);
    lt8911exb_write(client, 0x07, 0xff);
    lt8911exb_write(client, 0x0a, 0xfc);
    lt8911exb_write(client, 0x0a, 0xfe);

    /* link train */
    lt8911exb_write(client, 0xff, 0x85);
    lt8911exb_write(client, 0x1a, lt8911exb->lane_cnt);
    // lt8911exb_write(client,0x13,0xd1);
    lt8911exb_write(client, 0xff, 0xac);
    lt8911exb_write(client, 0x00, 0x64);
    lt8911exb_write(client, 0x01, 0x0a);
    lt8911exb_write(client, 0x0c, 0x85);
    lt8911exb_write(client, 0x0c, 0xc5);
}

void lt8911exb_config(struct i2c_client *client)
{
    lt8911exb_mipi_video_timing(client); //defualt setting is 1080P
    lt8911exb_edp_video_cfg(client);
    lt8911exb_setup(client);

    lt8911exb_video_check(client); //just for Check MIPI Input
    lt8911exb_link_train(client);
}

static int lt8911exb_probe(struct i2c_client * client, const struct i2c_device_id * id)
{
    int ret = -1;
    struct lt8911exb_data *lt8911exb;

    /* do NOT remove these logs */
    dev_info(&client->dev, "LT8911EXB Driver Version: %s\n", LT8911EXB_DRIVER_VERSION);
    dev_info(&client->dev, "LT8911EXB I2C Address: 0x%02x\n", client->addr);

    if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
        dev_err(&client->dev, "Failed check I2C functionality");
        return -ENODEV;
    }

    lt8911exb = devm_kzalloc(&client->dev, sizeof(*lt8911exb), GFP_KERNEL);

    if(lt8911exb == NULL) {
        dev_err(&client->dev, "Failed alloc lt8911exb memory");
        return -ENOMEM;
    }

    if(client->dev.of_node) {
        ret = lt8911exb_parse_dt(&client->dev, lt8911exb);

        if(ret < 0) {
            dev_err(&client->dev, "Failed parse dlt8911exb\n");
            goto exit_free_client_data;
        }
    }

    lt8911exb->client = client;
    i2c_set_clientdata(client, lt8911exb);

    ret = lt8911exb_request_io_port(lt8911exb);

    if(ret < 0) {
        dev_err(&client->dev, "Failed request IO port\n");
        goto exit_free_client_data;
    }

    lt8911exb_reset_guitar(client);
	
    ret = lt8911exb_i2c_test(client);

    if(ret < 0) {
        dev_err(&client->dev, "Failed communicate with IC use I2C\n");
        goto exit_free_io_port;
    }

    lt8911exb_config(client);
	mdelay(200);
    dev_info(&client->dev, "LT8911EXB setup finish.\n");

    return 0;

exit_free_io_port:

    if(gpio_is_valid(lt8911exb->rst_gpio))
        gpio_free(lt8911exb->rst_gpio);

    if(gpio_is_valid(lt8911exb->pwr_gpio))
        gpio_free(lt8911exb->pwr_gpio);

	if(gpio_is_valid(lt8911exb->bl_gpio))
        gpio_free(lt8911exb->bl_gpio);

exit_free_client_data:
    devm_kfree(&client->dev, lt8911exb);
    i2c_set_clientdata(client, NULL);

    return ret;
}

static int lt8911exb_remove(struct i2c_client * client)
{
    struct lt8911exb_data *lt8911exb = i2c_get_clientdata(client);

    if(gpio_is_valid(lt8911exb->rst_gpio))
        gpio_free(lt8911exb->rst_gpio);

    if(gpio_is_valid(lt8911exb->pwr_gpio))
        gpio_free(lt8911exb->pwr_gpio);
	
	if(gpio_is_valid(lt8911exb->bl_gpio))
        gpio_free(lt8911exb->bl_gpio);

    dev_info(&client->dev, "goodix lt8911exb driver removed");
    i2c_set_clientdata(client, NULL);

    devm_kfree(&client->dev, lt8911exb);

    return 0;
}

static const struct of_device_id lt8911exb_match_table[] = {
    {.compatible = "lontium,lt8911exb",},
    { },
};

static const struct i2c_device_id lt8911exb_device_id[] = {
    { LT8911EXB_I2C_NAME, 0 },
    { }
};

static struct i2c_driver lt8911exb_driver = {
    .probe      = lt8911exb_probe,
    .remove     = lt8911exb_remove,
    .id_table   = lt8911exb_device_id,
    .driver = {
        .name     = LT8911EXB_I2C_NAME,
        .owner    = THIS_MODULE,
        .of_match_table = lt8911exb_match_table,
    },
};


static int __init lt8911exb_init(void)
{
    s32 ret;

    pr_info("Lontium LT8911EXB driver installing....\n");
    ret = i2c_add_driver(&lt8911exb_driver);
    return ret;
}

static void __exit lt8911exb_exit(void)
{
    pr_info("Lontium LT8911EXB driver exited\n");
    i2c_del_driver(&lt8911exb_driver);
}

fs_initcall(lt8911exb_init);
module_exit(lt8911exb_exit);

MODULE_DESCRIPTION("Lontium LT8911EXB Driver");
MODULE_LICENSE("GPL V2");

kernel/msm-4.9/drivers/video/lt8911exb/lt8911exb.h
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/major.h>
#include <linux/kdev_t.h>
#include <linux/of_gpio.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/usb.h>
#include <linux/power_supply.h>

#define LT8911EXB_I2C_NAME      "lt8911exb"
#define LT8911EXB_DRIVER_VERSION  "1.0.0"

#define LT8911EXB_ADDR_LENGTH      1
#define I2C_MAX_TRANSFER_SIZE   255
#define RETRY_MAX_TIMES         3

struct lt8911exb_data {
    struct i2c_client *client;
    int pwr_gpio;
    int rst_gpio;
	int bl_gpio;

    int hact;
    int vact;
    int hbp;
    int hfp;
    int hs;
    int vbp;
    int vfp;
    int vs;
    int pclk;
    int htotal;
    int vtotal;

    int lane_cnt;
    int mipi_lane;
    int color; //Color Depth 0:6bit 1:8bit
    int test;
};

#endif /*_LONTIUM_LT8911EXB_H_*/

四、添加SDM450副屏部分参数,注意主控跟LT8911连接是几lane

1、屏幕头文件

kernel/msm-4.9/arch/arm64/boot/dts/qcom/dsi-panel-test-600-video.dtsi
&mdss_mdp {
	dsi_ydt101ml659hs24a_600_video: qcom,mdss_dsi_ydt101ml659hs24a_600_video {
		qcom,mdss-dsi-panel-name = "YDT101ML659HS24A 600 video mode dsi panel";
	//	qcom,mdss-dsi-panel-controller = <&mdss_dsi1>;
		qcom,mdss-dsi-panel-type = "dsi_video_mode";
	//	qcom,mdss-dsi-panel-destination = "display_2";
		qcom,mdss-dsi-panel-framerate = <60>;
		qcom,mdss-dsi-virtual-channel-id = <0>;
		qcom,mdss-dsi-stream = <0>;
		qcom,mdss-dsi-panel-width = <1366>;
		qcom,mdss-dsi-panel-height = <768>;
		qcom,mdss-dsi-h-front-porch = <94>;
		qcom,mdss-dsi-h-back-porch = <50>;
		qcom,mdss-dsi-h-pulse-width = <16>;
		qcom,mdss-dsi-h-sync-skew = <0>;
		qcom,mdss-dsi-v-back-porch = <13>;
		qcom,mdss-dsi-v-front-porch = <13>;
		qcom,mdss-dsi-v-pulse-width = <6>;
		qcom,mdss-dsi-h-left-border = <0>;
		qcom,mdss-dsi-h-right-border = <0>;
		qcom,mdss-dsi-v-top-border = <0>;
		qcom,mdss-dsi-v-bottom-border = <0>;
		qcom,mdss-dsi-bpp = <24>;
		qcom,mdss-dsi-color-order = "rgb_swap_rgb";
		qcom,mdss-dsi-underflow-color = <0xff>;
		qcom,mdss-dsi-border-color = <0>;
		qcom,mdss-dsi-on-command = [
                    05 01 00 00 78 00 02 11 00
                    05 01 00 00 32 00 02 29 00];
		qcom,mdss-dsi-off-command = [
                    05 01 00 00 32 00 02 28 00
					05 01 00 00 78 00 02 10 00];
		qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
		qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
		qcom,mdss-dsi-h-sync-pulse = <1>;
		qcom,mdss-dsi-traffic-mode = "burst_mode";
		qcom,mdss-dsi-lane-map = "lane_map_0123";
		qcom,mdss-dsi-bllp-eof-power-mode;
		qcom,mdss-dsi-bllp-power-mode;
		qcom,mdss-dsi-lane-0-state;
		qcom,mdss-dsi-lane-1-state;
		qcom,mdss-dsi-lane-2-state;
		qcom,mdss-dsi-lane-3-state;
		qcom,mdss-dsi-panel-timings = [82 1c 12 00 40 42 16 1e 14 03 04 00];
		qcom,mdss-dsi-t-clk-post = <0x04>;
		qcom,mdss-dsi-t-clk-pre = <0x27>;
		qcom,mdss-dsi-bl-min-level = <1>;
		qcom,mdss-dsi-bl-max-level = <4095>;
		qcom,mdss-dsi-dma-trigger = "trigger_sw";
		qcom,mdss-dsi-mdp-trigger = "none";
		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
		qcom,mdss-dsi-reset-sequence = <1 20>, <0 30>, <1 30>;
        qcom,mdss-dsi-panel-timings-phy-v2 = [
        1e 1b 04 06 02 03 04 a0    /*Data 0*/
        1e 1b 04 06 02 03 04 a0      /*Data 1*/
        1e 1b 04 06 02 03 04 a0    /*Data 2*/
        1e 1b 04 06 02 03 04 a0      /*Data 3*/
        1e 0e 04 05 02 03 04 a0];   /*CLK lane*/
	};
};

2、配置双屏显示模式,并使用mdss_dsi1为副屏显示

kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953-nopmi-panel-camera.dtsi
&mdss_dsi {
        hw-config = "dual_dsi"; //"dual_dsi"; //"single_dsi";
};


&mdss_dsi1 {
        status = "ok";
        qcom,dsi-pref-prim-pan = <&dsi_ydt101ml659hs24a_600_video>;
        pinctrl-names = "mdss_default", "mdss_sleep";
        pinctrl-0 = <&mdss_dsi1_active &mdss_te1_active>;
        pinctrl-1 = <&mdss_dsi1_suspend &mdss_te1_suspend>;

        qcom,bridge-index = <0>;
        qcom,pluggable;
};

&dsi_ydt101ml659hs24a_600_video {
        qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
        qcom,dba-panel;
        qcom,bridge-name = "dsi1-bridge";
};

3、配置DEFAULT_VFRMT_TIMING 

DEFAULT_VFRMT_TIMING 是高通SDM450副屏必须配置的一个参数

#define DEFAULT_VFRMT_TIMING                           \
       {DEFAULT_VFRMT, 1366/*active_h*/, 94/*front_porch_h*/, 16/*pulse_width_h*/, 50/*back_porch_h*/, false,  \
        768/*active_v*/, 13/*front_porch_v*/, 6/*pulse_width_v*/, 13/*back_porch_v*/, false, \
        73248/*pixel_freq/1000*/, 60000/*refresh_rate/1000*/, false, true, HDMI_RES_AR_16_9, 0}

73248 = (1366+94+16+50)*(768+13+6)*60/1000

如果这个参数不对系统会一直打印,然后不断变得很卡,最后系统会崩溃重启

[   61.361415] __mdss_fb_sync_buf_done_callback: mdp-fence: frame timeout
[   61.470309] mdss_mdp_video_pollwait: vsync poll timed out! rc=-110 status=0x0 mask=0x20000000
[   61.470360] __mdss_fb_sync_buf_done_callback: mdp-fence: frame timeout

五、总结

对于副屏mipi转EDP相对于主屏来说比较简单,只需要注意各个接口和硬件的匹配

 

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值