天天看點

MTK平台ATA測試LCM出現Not Support

一、問題描述:

使用MTK的ATA測試工具測試LCM時提示“Not Support”

ATA是指:Assembly(安裝) Test Assistant Tool 

背景介紹:

一供LCD:TCL模組,IC:ILI9806E

二供LCD:BOYI模組,IC:OTM8019A

二、問題分析:

1. 我司對ATA測試LCM有過客制化,即:隻要Probe成功,就立即傳回Pass,是以從次入手檢視,由于需要調用lcm的disp_lcm_probe函數,列印序列槽log來分析,從列印出來的log來看,進行測試的時候發現并未走到probe函數,即PC機和手機并未通信,此時懷疑是否有測試LCM的開關未開啟,送出Eservice咨詢MTK,發現果真如此,如下是MTK給出的答複:

LCM需要客制化,是以将此項注釋掉了,如果需要測試此項,需要添加:

在factory.ini裡面添加

MenuItem=顯示屏;

/vendor/mediatek/proprietary/custom/$project$/factory/inc/cust.h

#define FEATURE_FTM_LCM

是以是測試LCM的開關未開啟,導緻提示Not Supprot,将開關開啟進行測試,測試Pass

2. 開關開啟後在測試過程中又遇到了另外的一個問題,不插入LCD進行測試的時候竟然也能測試Pass??

由于我司隻有兩款LCD,是以在周遊LCD驅動隻周遊兩個,當都Probe失敗時給預設的ili9806e的驅動;下面是抓取的拔掉LCD時的序列槽log:

[    1.394421] <1>.(1)[1:swapper/0][kernel]:get_lcm_id=83.

[    1.395068] <1>.(1)[1:swapper/0][kernel]:disp_lcm_probe ,lcm_ic_id=0x83.

[    1.395903] <1>.(1)[1:swapper/0][kernel]:disp_lcm_probe ,lcd_id_voltage=3._lcm_count()=2

[    1.396912] <1>.(1)[1:swapper/0][kernel]:disp_lcm_probe ,check lcd:ili9806e_dsi_vdo_tcl_blu5039.lcd_id_voltage=3.

[    1.398192] <1>.(1)[1:swapper/0][kernel]:disp_lcm_probe ,detect lcd successfully.lcd_name=ili9806e_dsi_vdo_tcl_blu5039.

在未接入LCD時竟然能Probe成功,真是很神奇啊!

下面檢視log列印所對應的代碼片段:

disp_lcm_handle* disp_lcm_probe(char* plcm_name, LCM_INTERFACE_ID lcm_id)
{
	DISPFUNC();
	
	unsigned int v_i;
	unsigned int lcmindex = 0;
	bool isLCMFound = false;
	bool isLCMInited = false;
	int lcd_id0=0,lcd_id1=0,lcd_id=0;
	LCM_DRIVER *lcm_drv = NULL;
	LCM_PARAMS *lcm_param = NULL;
	disp_lcm_handle *plcm = NULL;
	//int is_need_read_ic_id=0;
	
	//printk("[kernel]:%s,start .\n",__func__);  
#ifdef GPIO_LCD_ID0_PIN
	mt_set_gpio_mode(GPIO_LCD_ID0_PIN, GPIO_MODE_00);
        mt_set_gpio_dir(GPIO_LCD_ID0_PIN, GPIO_DIR_IN);
        mt_set_gpio_pull_select(GPIO_LCD_ID0_PIN,GPIO_PULL_UP);
        mt_set_gpio_pull_enable(GPIO_LCD_ID0_PIN, GPIO_PULL_ENABLE);
	mdelay(10);
        lcd_id0 = mt_get_gpio_in(GPIO_LCD_ID0_PIN);
        mt_set_gpio_pull_select(GPIO_LCD_ID0_PIN,GPIO_PULL_DISABLE);
#endif
#ifdef GPIO_LCD_ID1_PIN
	mt_set_gpio_mode(GPIO_LCD_ID1_PIN, GPIO_MODE_00);
        mt_set_gpio_dir(GPIO_LCD_ID1_PIN, GPIO_DIR_IN);
        mt_set_gpio_pull_select(GPIO_LCD_ID1_PIN,GPIO_PULL_UP);
        mt_set_gpio_pull_enable(GPIO_LCD_ID1_PIN, GPIO_PULL_ENABLE);
	mdelay(10);
        lcd_id1 = mt_get_gpio_in(GPIO_LCD_ID1_PIN);
        mt_set_gpio_pull_select(GPIO_LCD_ID1_PIN,GPIO_PULL_DISABLE);
#endif
	lcd_id=(lcd_id1<<1)|lcd_id0;

	printk("[kernel]:%s ,lcm_ic_id=0x%x.\n",__func__,get_lcm_id());
	
	printk("[kernel]:%s ,lcd_id_voltage=%d._lcm_count()=%d\n",__func__,lcd_id,_lcm_count());

	for(v_i=0;v_i<_lcm_count();v_i++)
	{
		printk("[kernel]:%s ,check lcd:%s.lcd_id_voltage=%d.\n",__func__,lcm_driver_list[v_i]->name,lcm_driver_list[v_i]->lcd_id_voltage);
		if(lcm_driver_list[v_i]->lcd_id_voltage == lcd_id)
		{		
			lcm_drv = lcm_driver_list[v_i];
			if(lcm_drv->get_lcd_id_register)
			{

				if(lcm_drv->get_lcd_id_register()== get_lcm_id())
				{
					printk("[kernel]:%s ,detect lcd with read ic id success.lcd_name=%s.\n",__func__,lcm_driver_list[v_i]->name);
					isLCMFound = true;
				}
			}
			else
			{
				isLCMFound = true;
			}
			if (isLCMFound)
			{
				lcm_kernel_detect_drv=lcm_driver_list[v_i];		
				isLCMInited = true;
				lcmindex=v_i;
				break;
			}
		}
	}

	if(isLCMFound==false)
	{
		lcm_drv = lcm_driver_list[0];
		lcm_kernel_detect_drv=lcm_driver_list[0];
		isLCMFound = false;
		isLCMInited = true;
		lcmindex=0;
	}

	if(isLCMFound == false)
	{
		lcd_find_success=0;
		printk(KERN_ERR "[kernel]:%s ,detect lcd fail,use default.lcd_name=%s.\n",__func__,lcm_kernel_detect_drv->name);
	}
	else
	{
		lcd_find_success=1;
		printk(KERN_ERR "[kernel]:%s ,detect lcd successfully.lcd_name=%s.\n",__func__,lcm_kernel_detect_drv->name);
	}

	
	plcm = kzalloc(sizeof(uint8_t*) *sizeof(disp_lcm_handle), GFP_KERNEL);
	lcm_param = kzalloc(sizeof(uint8_t*) *sizeof(LCM_PARAMS), GFP_KERNEL);
	if(plcm && lcm_param)
	{
		plcm->params = lcm_param;
		plcm->drv = lcm_drv;
		plcm->is_inited = isLCMInited;
        	plcm->index = lcmindex;
	}
	else
	{
		DISPERR("FATAL ERROR!!!kzalloc plcm and plcm->params failed\n");
		goto FAIL;
	}
	
	{
		plcm->drv->get_params(plcm->params);
		plcm->lcm_if_id = plcm->params->lcm_if;

		// below code is for lcm driver forward compatible
		if(plcm->params->type == LCM_TYPE_DSI && plcm->params->lcm_if == LCM_INTERFACE_NOTDEFINED) plcm->lcm_if_id = LCM_INTERFACE_DSI0;
		if(plcm->params->type == LCM_TYPE_DPI && plcm->params->lcm_if == LCM_INTERFACE_NOTDEFINED) plcm->lcm_if_id = LCM_INTERFACE_DPI0;
		if(plcm->params->type == LCM_TYPE_DBI && plcm->params->lcm_if == LCM_INTERFACE_NOTDEFINED) plcm->lcm_if_id = LCM_INTERFACE_DBI0;

		if((lcm_id == LCM_INTERFACE_NOTDEFINED) || lcm_id == plcm->lcm_if_id)
		{
			plcm->lcm_original_width = plcm->params->width;
			plcm->lcm_original_height = plcm->params->height;
			_dump_lcm_info(plcm);
			return plcm;
		}
		else
		{
			DISPERR("the specific LCM Interface [%d] didn't define any lcm driver\n", lcm_id);
			goto FAIL;
		}
	}

FAIL:
	
	if(plcm) kfree(plcm);
	if(lcm_param) kfree(lcm_param);
	return NULL;
}
           

代碼邏輯是通過讀取GPIO口的狀态來擷取IC的ID;

lcd_id0 = mt_get_gpio_in(GPIO_LCD_ID0_PIN);
           
lcd_id1 = mt_get_gpio_in(GPIO_LCD_ID1_PIN);
           
lcd_id=(lcd_id1<<1)|lcd_id0;
           

從log看LCD_ID=3,是以lcd_id0=1,lcd_id1=1,即從GPIO口讀取的電壓狀态是高電平,檢視ili9806e的驅動發現,ili9806e的ID預設是3,導緻識别錯誤,下面是ili9806e的驅動注冊代碼:

LCM_DRIVER ili9806e_dsi_vdo_tcl_blu5039_drv = 
{
    .name			= "ili9806e_dsi_vdo_tcl_blu5039",
	.set_util_funcs = lcm_set_util_funcs,
	.compare_id     = lcm_compare_id,
	.get_params     = lcm_get_params,
	.init           = lcm_init,
	.suspend        = lcm_suspend,
	.resume         = lcm_resume,
	.set_backlight      = lcm_setbacklight_tcl,
#if defined(LCM_DSI_CMD_MODE)
    .update         = lcm_update,
#endif
	.init_power                = lcm_init_power,
	.lcd_id_voltage = 3,
    .lcm_ic_id = get_lcm_id,
    };
           

未插入LCD時GPIO口處于懸空狀态,即高阻态,是以輸出的是高電平,導緻驅動識别LCD出現錯誤的現象;

三、問題解決方式

每個LCD IC内部還有一個IC ID,通過讀取此ID進一步獲知外接LCD的類型和是否存在;

現在的想法是在LK階段擷取IC ID,将IC ID寫入到cmdline,啟動到Kernel階段時,一是讀取GPIO擷取ID,二是讀取cmdline這個條件,通過獲知這兩個判斷條件就可以區分出是哪款屏;

代碼實作如下:

vendor/mediatek/proprietary/bootable/bootloader/lk/dev/lcm/ili9806e_dsi_vdo_tcl_blu5039/ili9806e_dsi_vdo_tcl_blu5039.c

增加擷取IC ID的接口,并将擷取到的ID寫入到lcm_id變量中,友善将IC ID外傳,即:

static unsigned int lcm_compare_id(void)
{
	int   array[4];
	char  buffer[3];
	int  id0=0;
	int  id1=0;
	//char  id2=0;
    int  id=0;
    
	lcm_init_power();
    
	mt_set_gpio_mode(GPIO_LCD_RST_PIN, 0);
	mt_set_gpio_out(GPIO_LCD_RST_PIN, GPIO_OUT_ONE);
	mdelay(10);;
	mt_set_gpio_out(GPIO_LCD_RST_PIN, GPIO_OUT_ZERO);
    MDELAY(10);
	mt_set_gpio_out(GPIO_LCD_RST_PIN, GPIO_OUT_ONE);
    mdelay(120);
	
    array[0]=0x00063902;
    array[1]=0x0698ffff;
    array[2]=0x00000104;
    dsi_set_cmdq(array, 3, 1);
    MDELAY(10);
	
    array[0]=0x00013700;//0x00023700;
    dsi_set_cmdq(array, 1, 1);
    read_reg_v2(0x00, &id0, 1);
	
	
    array[0]=0x00013700;//0x00023700;
    dsi_set_cmdq(array, 1, 1);
    read_reg_v2(0x01, &id1, 1);

    id = (id0<<8)|id1;
    lcm_id = id;
  
#ifdef BUILD_LK
	printf("[uboot] %s, id0 = 0x%08x, id1 = 0x%08x, id = 0x%08x\n", __func__, id0, id1, id);
#endif
	
	if(id == ILI9806E_ID)
	    return 1;
	else
  	    return 0;
}
           
static int get_lcm_id(void)
{
	return lcm_id;
}
           

中間是怎麼傳到cmdline中的過程比較簡單,自己寫方可,也可以參看附件代碼,最後寫到cmdline中的代碼如下:

vendor/mediatek/proprietary/bootable/bootloader/lk/app/mt_boot/mt_boot.c

sprintf(cmdline_tmpbuf, "%s%s%d", CMDLINE_TMP_CONCAT_SIZE, " lcm_id=", mt_disp_get_lcm_ic_id());
    cmdline_append(cmdline_tmpbuf);
           

這樣在kernel階段會自動解析cmdline,并在裡面識别lcm_id字段:

kernel-3.18/drivers/misc/mediatek/mach/mt6735/bootinfo.c

int lcm_id=0x83;
static int __init lcm_id_setup(char *str)
{
        int en;
        if(!get_option(&str, &en))
                return 0;
        lcm_id = en;
        return 1;
}
int get_lcm_id(void)
{
        printk("[kernel]:get_lcm_id=%x.\n",lcm_id);
        return lcm_id;
}
__setup("lcm_id=", lcm_id_setup);
           

__setup會在Kernel階段自動解析cmdline,即lcm_id_setup()函數來解析cmdline中的“lcm_id”

備注:

有些人會問為何要在lk階段讀取寄存器,而不是在Kernel階段,之是以這樣做是因為之前有過這樣做過,但是從寄存器裡面讀取的是空,導緻讀取異常,估計是Kernel階段剛剛啟動,雖然LK已經将硬體環境初始化的差不多了,但是此時軟體環境還未搭建好,導緻異常的發生,暫時隻能從這個方面去猜測了