导文,在手机屏幕调试过程中,经常会遇到屏的ECN器件存在色温差异问题,举个栗子:一供和二供屏在纯白颜色界面有着很明显的色温差异。那么有的同仁可能会疑问,为什么不让两个ECN器件保持同样的色温标准呢?答案是目前行业中,很少有哪家模组厂能够保证模组的一致性非常好,且固定色温偏差在一个很小的范围内;另外,不同的模组厂之间的物料也会有差异,所以很常见的就是不同模组厂出货的屏幕色温会存在差异,更甚至有同一批次的模组有些色温的差异会达到500-1000K。这个时候,从软件层面来优化这种类似问题的需求便应势而生了,PictureQuality(简称 :PQ),便是软件调试接口,是一个提高图像质量的一个新功能,为用户提高一个良好的使用体验。
调试Gamma和色温,需要分2步走,第一步是需要把效果调试好,第二部是把调试好的参数导出,合入到项目工程中去,并做好屏幕的识别处理。
第一步:获取到MTK的PQ调试工具——MiraVision_Tuning_Kit
工具的下载路径在MTK的online网址上可以找到,这里附上链接:mtk工具下载路径:
在里面可以通过搜索找到MiraVision_Tuning_Kit,建议下载前先查看下右边的ReleaseNote看下平台是不支持调试工具
向MTK提供申请下载工具,然后等到MTK回复下载工具的ID,然后点开链接去下载即可
建议拿到工具后优先看下工具中自带的调试指导文档,新手必看教程:
第二步:准备调试机器的调试环境
主要有以下注意事项:
1、调试机器的软件版本必须是userdebug版本,并且需要在开发者模式中打开ADB调试功能 2、在手机拨号盘上输入MTK的调试暗码:*#*#3646633#*#*(可能不同的第三方开发商会修改成自己的暗码,例如我这边就是*#*#9646633#*#*),进入EngineerMode,左右滑动到Log and Debugging 菜单栏目下点击 ATCI,点击 Always enable ATCI。 3、安装工具中自带的MTK驱动文件。(建议用Windows系统电脑来进行调试,因为虚拟机有时候不好使) 4、调试机器用usb与电脑链接,然后在打开调试工具前先跑一下工具中自带的脚本Before_Tuning.bat 5、双击打开调试工具MiravisionTuningKit.exe
另外还有一些注意事项需要提前准备,就是需要手动关闭AAL /PQ/CABC和自动调节亮度功能,因为有这些功能开启的情况下会影响采集RAWData,把这些影响背光和色彩的特殊功能关闭了,MTK这边才能采集到调试机器比较干净的色彩数据。
做完以上准备工作之后,下一步就是采集Gamma的RAWData了,在调试工具中其实自带了一份Gamma RAWData的表格模板,我们先要通过色彩分析仪例如CA210/CA310/CA410来测量机器的Gamma数据,这里有一点非常重要:R/G/B/W四种颜色的Gamma都需要采集 ,否则会影响调试效果!按照常见的测试方法,我们通常以白色Gamma曲线作为评估一块屏幕的Gamma是否OK,但是严格意义上应该以R/G/B/W四种颜色的全部Gamma曲线来评估。在调PQ的时候如果漏掉了其中任何一个颜色的Gamma数据,调试出来的效果都是非常差的。
至于测量步阶,以个人工作需要为准,如果采用步阶4,那就是每4个灰阶测一次灰阶亮度,这样0~255灰阶就会有65组数据。这里步阶越小,采集到的数据就会更详细,花的时间就会多一些。分别把R/G/B/W四颜色Gamma曲线测量出来后将X/Y/Z三列数据填到调试工具自带的模板表格中去。
第三步:进入调试工具开始调试
进入调试工具界面会要求选择对应的芯片平台、软件平台和MTK设备端口号,按照调试机器的匹配类型去选择好,然后点击后面的感叹号就会开始连接手机了,
连接成功了后感叹号就会有个绿色的护身符,然后把优先把CE Config和CCORR的使能开关勾选掉
先调试色温
点击主菜单上面的parttern Gen,接着选择Pattern Gen,然后选择Gen by AAL,再勾选上下面的pattern Gen,就可以手动调试了.
调试色温的时候,可以先点一下右边的W,屏幕会变成纯白色,然后可以适当调一下Red/Green/Blue三种颜色的灰阶数,每调一次就借助色彩分析仪来测一下色温是否满足需求。通常情况下Green灰阶对最大亮度的影响最大,如果降低太多也许会使得最大亮度降很多,这里举个例子,假如我测量R/G/B的灰阶数分别为245/255/255的时候,测量出来色温达到我的需求后,我便把R/G/B的灰阶数记下来。
然后调试Gamma
调试gamma步骤如下:
1、点击上面菜单栏的Gamma按钮 2、RawData这里选择Gamma曲线的步阶,这个步阶一定需要和你前面步骤测R/G/B/W颜色Gamma Raw Data的步阶保持一样 3、点击Import Raw Data按钮,文件选择前面填好的GammaRawData.xlsx 4、把后面的R/G/B/W的选框全都勾选上,一定要勾选上啊!!😂😂😂别问为什么 5、填一下下面的R/G/B灰阶数,填前面满足色温的灰阶参数 6、Gamma Value选择你的目标Gamma曲线 7、在最下面Para Gen and output里面点击Para Gen,调试工具就会根据你前面导入的GammaRawData数据来调试出与目标Gamma曲线相符合的Gamma参数,当然,关于目标色温的也会一块合进去。 8、左边Hardware下把Enable Gamm选框勾选上,然后点一下Write按钮就可以将调试好的Gamma和色温参数写到手机中生效,约几十帧的时间就会生效(但是亮灭屏后擦除恢复),写入到手机中后就可以立马用色彩分析仪测量一下,看看Gamma曲线和色温是否达到你的期望值,这里建议考虑到模组的一致性问题,选择适合的参数 9、点击最下面的.tbl Output按钮,就可以将此次调试的参数导出。
来看一下调试前后的对比
调试前的Gamma曲线图:
调试后的Gamma曲线图:
效果是显而易见的,然后前后色温也会改变,建议测色温前保持亮屏热机5~10分钟后再测
接下来一看看前面调试好参数导出来后像什么样子,这里面会包含R/G/B各自的参数
第四部:将调试好的参数合入到工程中去
相关文件代码的路径也许会因为不同平台存在差异,我这里以mt6765的代码做讲解
客制化参数文件
文件路径:alps\vendor\mediatek\proprietary\custom\mt6765\hal\pq\cust_gamma.cpp
里面有一个名叫‘cust_gamma 指针数组,其中0~14共有15组参数,每组里面都有R/G/B的参数,如下图:
我们将调试好的参数放到这其中一个数组中即可,例如Gamma0当中,但是需要注意放对位置,不要把R颜色的参数放到G颜色参数的位置去了😂
PQ文件配置文件
PQ相关的主要流程在这个PictureQuality.cpp这个文件夹内,可能有2个文件区分legacy和fusion
路径在:alps\vendor\mediatek\proprietary\hardware\pq\v2.0\lib\service
上面提到的配置客制化参数的位置,其默认是用的第8组数据,也就是cust_gamma [7],代码中定义如下:
// The index of default gamma table #define GAMMA_INDEX_DEFAULT 7
主要的流程如下:
在bool PictureQuality::threadLoop()中初始化,调用loadGammaEntryTable()
#ifdef MTK_DISP_GAMMA_SUPPORT if (mDispPQPropertyValue[DISP_GAMMA_SUPPORT]) loadGammaEntryTable(); #endif
在loadGammaEntryTable()中主要是去获取客制化gamma的参数,并开辟内存空间
#ifdef MTK_DISP_GAMMA_SUPPORT void PictureQuality::loadGammaEntryTable() { // load gamma from cust lib if load factory gamma fail or factory gamma not support CustParameters &cust = CustParameters::getPQCust(); gamma_entry_t* ptr = (gamma_entry_t*)cust.getSymbol("cust_gamma"); if (!ptr) { PQ_LOGD("[PQ_SERVICE] cust_gamma is not found in libpq_cust.so\n"); } else { memcpy(m_CustGamma, ptr, sizeof(gamma_entry_t) * GAMMA_LCM_MAX * GAMMA_INDEX_MAX); memcpy(m_NvGamma, ptr, sizeof(gamma_entry_t) * GAMMA_LCM_MAX * GAMMA_INDEX_MAX); } #ifdef FACTORY_GAMMA_SUPPORT if (loadNvGammaTable() == NO_ERROR) { gamma_entry_t *entry = (m_NvGamma[0]); configGamma(m_PQMode, entry); return; } #endif if (ptr) { gamma_entry_t *entry = (m_CustGamma[0]); configGamma(m_PQMode, entry); } } #endif ```
然后调用configGamma(int picMode, const gamma_entry_t *entry)去索引选用哪个gammaIndex,所以我们要做LCD兼容区分的时候,建议就在这里去做区分。
void PictureQuality::configGamma(int picMode, const gamma_entry_t *entry) { #ifdef MTK_DISP_GAMMA_SUPPORT #if (GAMMA_LCM_MAX > 0) && (GAMMA_INDEX_MAX > 0) int gammaIndex = 0; #endif #if GAMMA_INDEX_MAX > 1 // get gamma index from runtime property configuration char property[PROPERTY_VALUE_MAX] = {0}; gammaIndex = GAMMA_INDEX_DEFAULT; if (picMode == PQ_PIC_MODE_USER_DEF && property_get(GAMMA_INDEX_PROPERTY_NAME, property, NULL) > 0 && strlen(property) > 0) { gammaIndex = atoi(property); } if (gammaIndex < 0 || GAMMA_INDEX_MAX <= gammaIndex) gammaIndex = GAMMA_INDEX_DEFAULT; #endif #if (GAMMA_LCM_MAX > 0) && (GAMMA_INDEX_MAX > 0) PQ_LOGD("Gamma index: %d/%d", gammaIndex, GAMMA_INDEX_MAX); configDriverGamma(entry + gammaIndex); #endif #endif }
关于屏的识别区分方案
在一个项目中不同模组厂出货的屏基本上都会存在差异,所以我们在调试PQ时务必要做好屏幕的识别区分,否则会影响另外模组的主观表现,这里简单介绍两个区分与方案:
1.通过索引lcm_index来区分
可以在上面的configGamma函数里面去调用_getLcmIndexOfGamma来获取lcm_index,理论上这个lcm_index最终是从底层获得,相关代码参考如下:
void PictureQuality::configGamma(int picMode, const gamma_entry_t *entry) { #ifdef MTK_DISP_GAMMA_SUPPORT #if (GAMMA_LCM_MAX > 0) && (GAMMA_INDEX_MAX > 0) int gammaIndex = 0; #endif int lcm_index=_getLcmIndexOfGamma();//add by Shoulder.Newan for recognize if(lcm_index == 0) //如果是一供LCM gammaIndex = GAMMA_INDEX_DEFAULT;//这里去配置选用cust_gamma中的哪组参数 else if(lcm_index == 1)//如果是二供 gammaIndex = GAMMA_INDEX_DEFAULT;//这里去配置选用cust_gamma中的哪组参数 else gammaIndex = GAMMA_INDEX_DEFAULT;
_getLcmIndexOfGamma()函数会通过ioctl(m_drvID, DISP_IOCTL_GET_LCMINDEX, &lcmIdx);的方式去获取lcm的编号,
alps\kernel-4.19\drivers\misc\mediatek\video\mt6765\videox\mtk_disp_mgr.c
case DISP_IOCTL_GET_LCMINDEX: { return primary_display_get_lcm_index(); }
alps\kernel-4.19\drivers\misc\mediatek\video\mt6765\videox\primary_display.c
int primary_display_get_lcm_index(void) { int index = 0; DISPFUNC(); if (pgc->plcm == NULL) { DISPERR("lcm handle is null\n"); return 0; } index = pgc->plcm->index; DISPDBG("lcm index = %d\n", index); return index; }
alps\kernel-4.19\drivers\misc\mediatek\video\mt6765\videox\disp_lcm.c
struct disp_lcm_handle *disp_lcm_probe ...... for (i = 0; i < _lcm_count(); i++) { lcm_drv = lcm_driver_list[i]; if (!strcmp(lcm_drv->name, plcm_name)) { isLCMFound = true; isLCMInited = true; lcmindex = i; break; } } ...... plcm->index = lcmindex;
2.通过识别LCM的名字来区分
建议可以去读LCM的驱动节点lcm_name去做匹配,可能有些ODM创建的驱动节点路径各不相同,这里只做举例
假如我这边的LCM的驱动节点路径是:sys/devices/platform/soc/14014300.dsi/lcm_name
#define LCM_NAME_PATH "/sys/devices/platform/soc/14014300.dsi/lcm_name" #define LCM_NAME_PQ1 "ili8963_hdp_dsi_vdo_120hz" void PictureQuality::configGamma(int picMode, const gamma_entry_t *entry) { int ret; int retval; int lcm_index; #ifdef MTK_DISP_GAMMA_SUPPORT #if (GAMMA_LCM_MAX > 0) && (GAMMA_INDEX_MAX > 0) int gammaIndex = 0; #endif ret = open(LCM_NAME_PATH, O_RDONLY); if (ret >= 0) { char buffer[20]; retval = read(ret, buffer, sizeof(buffer)); if (retval <= 0) PQ_LOGE("read lcm driver node path failed"); else if(0 == strncmp(buffer, "LCM_NAME_PQ1", strlen("LCM_NAME_PQ1"))) { PQ_LOGE("We will load gamma for LCM_NAME_PQ1"); lcm_index = 1; } close(ret); break; } gammaIndex = lcm_index; ......
但是这种方式的话肯定会遇上avc权限问题,需要再处理一下avc权限相关,不过也并不复杂,看avc log前先输入指令:adb shell setenforce 0 ,然后再logcat | grep avc --color ,这里就不展开说了