MTK6589平台——“长按powerkey重启”feature不工作问题的解决

一、问题背景

普通的Android设备都有这样的一个feature:长按powerkey 8s就可以让系统自动重启。这样的一个feature是MTK平台本身就有的,目的是为了快速重启或者死机强制重启。但是目前试产的机器有这样的一个gap——产线上生产出来的机器要在没有接LCM的情况下进行长按powerkey的测试,但是产线上的此项测试并不是为了重启。所以现在的解决方案是:在没有接LCM的情况下,长按powerkey不重启;接LCM的情况下,长按powerkey重启。

现在的问题在于,生产组装完成的手机,即已经接了LCM的手机,长按powerkey也不能重启。这个问题基本可以出在两个方面:

(1) 本身重启的逻辑错误。就是说LCM已经检测到了,但是此feature的逻辑混乱了。

(2)LCM虽然接上了,但是detection出现问题。也就是说,feature逻辑没有问题,但是因为detect不到LCM,手机会认为你是在产线上做没有LCM前提下的长按powekey的测试。

二、问题解决 

问题的分析原因是有两个方面,所以要从两个方面开始分析。

1. feature的逻辑分析

主要code在bsp/mediatek/platform/mt6589/kernel/drivers/keypad/kpd.c,即keypad的kernel驱动。

(1)添加一个宏开关XZHANG_LONG_POWER_RESET,添加判断LCM是否存在的函数。

#ifdef XZHANG_LONG_POWER_RESET
extern char temp_command_line[1024];
extern int force_get_tbat(void);
#define NO_TEMP_VOLT -20

int get_cmdline_lcm_meta(void)
{
	char *p;
	p = strstr(temp_command_line, "lcm_meta=1");
	if(p == NULL) {
		return 0;
	} else {
		return 1;
	}
}

int is_batt_temp_pin(void)
{
    if(force_get_tbat() == NO_TEMP_VOLT) {
        printk("is_batt_temp_pin NO_TEMP_VOLT");
        return 0;
    } else {
        return 1;
    }
}
#endif

get_cmdline_lcm_meta()通过temp_command_line判断是否存在“lcm_meta=1",即是否存在lcm。temp_command_line最初是由bootloader的commandline传入的。

(2)enable LONG_POWER_RESET的实现,在boot mode加入长按powerkey的逻辑。

/*long press reboot function realize*/
	if(kpd_enable_lprst&& get_boot_mode() == NORMAL_BOOT) {
		kpd_print("Normal Boot long press reboot selection\n");
		kpd_print("Enable normal mode LPRST\n");

	if(get_cmdline_lcm_meta()) {
		kpd_print("enable LPRST\N");
		upmu_set_rg_pwrkey_rst_en(0x01);//pmic_config_interface(TOP_RST_MISC, 0x01, PMIC_RG_PWRKEY_RST_EN_MASK, PMIC_RG_PWRKEY_RST_EN_SHIFT);
		upmu_set_rg_homekey_puen(0x01);
		upmu_set_rg_homekey_rst_en(0x00);
		upmu_set_rg_pwrkey_rst_td(KPD_PMIC_LPRST_TD);
	} else {
		kpd_print("meta disable other mode LPRST\n");
		upmu_set_rg_pwrkey_rst_en(0x00);//pmic package function for long press reboot function setting
		upmu_set_rg_homekey_rst_en(0x00);
		upmu_set_rg_homekey_puen(0x01);
	}


(3)从keypad的驱动来看,是可以enable LONG_POWER_RESET feature的。主要原因是没有其他人修改这部分代码,而且是通过函数的向下调用关系也是没有问题的。

那么下一步可以怀疑temp_command_line中是没有”lcm_meta=1“的,也就是说可能bootloader所检测到的commandline是有问题的,或者就是说传入的过程中有问题。

2. commandline detection

从以上的分析继续往下看的话,完全可以怀疑是bootloader的commandline是有问题的,因为我看过传入的过程,没问题(这部分略过)。如前所述,commandline是由bootloader所获得并传给kernel的。

(1) commandline的获取是由bootable/bootloader/lk/app/mt_boot/mt_boot.c得到的,在boot_linux_from_storage()中,具体看一下:

    custom_port_in_kernel(g_boot_mode, commanline);
    strlen += sprintf(commanline, "%s lcm=%1d-%s", commanline, DISP_IsLcmFound(), mt_disp_get_lcm_id());
	#ifdef XZHANG_LONG_POWER_RESET
	if(mt_disp_compare_lcm_ic_id()){
		strlen += sprintf(commanline, "%s lcm_meta=%1d", commanline, 1);
	}else{
		strlen += sprintf(commanline, "%s lcm_meta=%1d", commanline, 0);
	}
	#endif
    strlen += sprintf(commanline, "%s fps=%1d", commanline, mt_disp_get_lcd_time());
    strlen += sprintf(commanline, "%s lcm_type=%1d", commanline,  mt_disp_get_lcd_type());

    boot_linux((void *)CFG_BOOTIMG_LOAD_ADDR, (unsigned *)CFG_BOOTARGS_ADDR,
		   (char *)commanline, board_machtype(),
		   (void *)CFG_RAMDISK_LOAD_ADDR, g_rimg_sz);

}

这样就看的更清楚了,继续往下看 mt_disp_compare_lcm_ic_id()。

(2) 如果一直往下分析mt_disp_compare_lcm_ic_id()的话,可以找到这里(bsp/mediatek/platform/mt6589/lk/disp_drv.c):

BOOL DISP_Compare_LCM_IC_ID(void)
{
printf("[LCM_IC_ID] %s \n",__func__);
    if(lcm_drv && lcm_drv->compare_ic_id)
        return lcm_drv->compare_ic_id();
    else
        return 0;
}

根据上面的分析,如果mt_disp_compare_lcm_ic_id()返回1的话,commanline才会写入”lcm_meta=1"。但是在这里的话,必须lcm_drv非空,而且lcm_drv的成员函数compare_ic_id()返回值也必须是非0的。

lcm_drv其实是lcm的驱动,即这里要求是lcm驱动是有的,且和硬件LCM是匹配的。lk/disp_drv.c中通过一个函数去得到平台上所定义的LCM的型号,然后得到对应的LCM驱动,从这里可以看得很清楚:

const LCM_DRIVER *disp_drv_get_lcm_driver(const char *lcm_name)
{
	LCM_DRIVER *lcm = NULL;
	printk("[LCM Auto Detect], we have %d lcm drivers built in\n", lcm_count);
	printk("[LCM Auto Detect], try to find driver for [%s]\n",
			(lcm_name==NULL)?"unknown":lcm_name);

	if(lcm_count ==1)
	{
		// we need to verify whether the lcm is connected
		// even there is only one lcm type defined
		lcm = lcm_driver_list[0];
		lcm->set_util_funcs(&lcm_utils);
		lcm->get_params(&s_lcm_params);
		u4IndexOfLCMList = 0;

		lcm_params = &s_lcm_params;
		lcm_drv = lcm;

		{
			isLCMFound = TRUE;
		}

        printk("[LCM Specified]\t[%s]\n", (lcm->name==NULL)?"unknown":lcm->name);

		goto done;
	}
	else 
	{
		int i;
		for(i = 0;i < lcm_count;i++)
		{
			lcm_params = &s_lcm_params;
			lcm = lcm_driver_list[i];

			printk("[LCM Auto Detect] [%d] - [%s]\t", i, (lcm->name==NULL)?"unknown":lcm->name);

			lcm->set_util_funcs(&lcm_utils);
			memset((void*)lcm_params, 0, sizeof(LCM_PARAMS));
			lcm->get_params(lcm_params);

			disp_drv_init_ctrl_if();
			disp_drv_set_driving_current(lcm_params);
			disp_drv_init_io_pad(lcm_params);

			if(lcm_name != NULL)
			{
				if(!strcmp(lcm_name,lcm->name))
				{
					printk("\t\t[success]\n");
					isLCMFound = TRUE;
                                   u4IndexOfLCMList = i;
					lcm_drv = lcm;

					goto done;
				}
				else
				{
					printk("\t\t[fail]\n");
				}
			}
			else
			{
				if(LCM_TYPE_DSI == lcm_params->type){
					init_dsi(FALSE);
					dsi_enable_power(TRUE);
				}

				if(lcm->compare_id != NULL && lcm->compare_id())
				{
					printk("\t\t[success]\n");
					isLCMFound = TRUE;
					lcm_drv = lcm;
                                   u4IndexOfLCMList = i;
					goto done;
				}
				else
				{

					lcm_drv = lcm;
					if(LCM_TYPE_DSI == lcm_params->type)
						DSI_Deinit();
						printk("\t\t[fail]\n");
				}
			}
		}
	}
done:
	return lcm_drv;
}

这个问题中使用的MT6589平台的手机是有两个驱动,分别是

#if defined(NT35595_FHD_DSI_CMD_CHIMEI)
	&nt35595_fhd_dsi_cmd_chimei_lcm_drv,
#endif

#if defined(NT35595_FHD_DSI_CMD_LG)
	&nt35595_fhd_dsi_cmd_lg_lcm_drv,

且lcm_count= sizeof( lcm_driver_list)/ sizeof( LCM_DRIVER*),那么lcm_count=2,从log也可以看到。

这里首先得到了lcm_drv,然后通过它去调用对应的compare_ic_id()。因为LCM_DRIVER是标准的,compare_ic_id()只是驱动的一个接口,那么接下来直接看对应的驱动程序的接口就行了。

(3)对于这个问题中的MT6589平台手机,使用的是lg lcm,其实也可以去bsp/mediatek/custom/common/kernel/lcm/目录下看一下平台上有哪些驱动。对于t35595_fhd_dsi_cmd_lg驱动来说,compare_ic_id的接口是lcm_compare_ic_id()。

static unsigned int lcm_compare_ic_id(void)
{

	unsigned int id=0;
	unsigned char buffer[2];
	unsigned int array[16];
	array[0] = 0x00023700;// read id return two byte,version and id
	dsi_set_cmdq(array, 1, 1);

	read_reg_v2(0xF4, buffer, 2);
	id = buffer[0]; //we only need ID
    #ifdef BUILD_LK
		printf("%s, id = 0x%x\n", __func__, id);
    #else
		printk("%s, id = 0x%x\n", __func__, id);
    #endif

    if(id == LCM_ID_NT35595)
    	return 1;
    else
        return 0;
}

其实走到这里,逻辑上已经很清楚了:

read_reg_v2()读到LCM ID,存在buffer中,然后给id。如果id == LCM_ID_NT35595,那么返回1,然后回到最初的地方,"lcm_meta=1"被写入commandline,然后正常传入kernel,“长按powerkey重启”也就能work。

这里的问题会出在:

从code看到,有人将LCM的模式从command mode改成了video mode。然后就和lg LCM的FAE沟通,看这个改动是不是有问题。

沟通结果知道video mode下,在LCM刷完一帧之后,其LCM id是读不出来的。这样,虽然bootloader给kernel传入正确的commandline,但是从kernel中读取commandline时,因为LCM id读取错误,导致了commandline中”lcm_meta=0"被读取了。

(4) 这里的解决思路是,在读取LCM id之前重新向LCM寄存器中写入正确的id,然后再读取其值。这样不管是刷完一帧之前还是之后,都能读取正确的lcm id

这里是重新定义了一个compare_id( ),然后替代compare_ic_id( )函数的功能读取正确LCM id:

static unsigned int lcm_compare_id(void)
{
        unsigned int ret = 0;
        unsigned char buffer[2];

        TC358768_DCS_write_1A_1P(0xFF,0x10);// CMD1
        MDELAY(1);

        read_reg_v2(0xF4, buffer,2);
#ifdef BUILD_LK
        printf("[IC_ID] %s 0xF4 0=0x%x 1=0x%x \n",__func__,buffer[0],buffer[1]);

#endif
        lcm_id = buffer[0];
        ret = mt_get_gpio_in(GPIO154);
#if defined(BUILD_LK)
        printf("%s, [IC_ID]nt35595 lg GPIO154 = %d \n", __func__, ret);
#endif

        return (ret == 1)?1:0;
}

这里TC358768_DCS_write_1A_1P(0xFF,0x10)是向lcm寄存器写入参数的操作,具体为什么是那两个参数,就需要去看LCM manual了。

然后在lcm_compare_ic_id( )中,修改如下:

static unsigned int lcm_compare_ic_id(void)
{

        unsigned int id=0;
        unsigned char buffer[2];
        unsigned int array[16];

        array[0] = 0x00023700;// read id return two byte,version and id
        dsi_set_cmdq(array, 1, 1);

        read_reg_v2(0xF4, buffer, 2);
        id = buffer[0]; //we only need ID
    #ifdef BUILD_LK
                printf("%s, id = 0x%x 0x%x \n", __func__, id,lcm_id);
    #else
                printk("%s, id = 0x%x 0x%x \n", __func__, id,lcm_id);
    #endif

 if(lcm_id==LCM_ID_NT35595) return 1; else if(id == LCM_ID_NT35595) return 1;
    else
        return 0;
}
这样的话,lcm_id中读取到正确值,然后kernel状态下长按powekey就可以重启。

三、小结

最后问题是可以这样解决的,但是lg LCM的FAE说video mode的效果不好,建议重新改回command mode,然后会改回了command mode,问题结束。

你可能感兴趣的:(kernel,LCM,bootloader,MT6589)