/* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define LOG_TAG "QCameraPerf" /* Includes */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include #include #include "mm_camera_dbg.h" #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #include "cam_types.h" #include "QCameraCommon.h" #include "QCameraPerfTranslator.h" static FSMDB_t *FSM=NULL; /* Functions */ static int32_t readlines(FILE *fp,char *table){ int32_t linesize=0; int32_t lines=0; char value; while(fread((char*)&value,1,1,fp)){ if(value == '\r'){ continue; } else if (value == '\n') { table[linesize]='\0'; lines++; return linesize; } else { table[linesize]=value; linesize++; } if(linesize > LINESIZE || lines > LINES) { LOGE("ERROR: Size allocated for tables exceeded: lines -> %d, linesize -> %d",lines,linesize); } } return 0; } /********* Get Params from strcut *********/ static int32_t get_type2( void *param1, __attribute__ ((unused))void *param2, int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; int32_t temp; temp=p1->type[streamNo]; return temp; } static int32_t get_istype2( void *param1, __attribute__ ((unused))void *param2, int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; int32_t temp; temp=p1->is_type[streamNo]; return temp; } static int32_t get_width2( void *param1, __attribute__ ((unused))void *param2, int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; int32_t temp; temp=p1->stream_sizes[streamNo].width; return temp; } static int32_t get_height2( void *param1, __attribute__ ((unused))void *param2, int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; int32_t temp; temp=p1->stream_sizes[streamNo].height; return temp; } static int32_t get_format2( void *param1, __attribute__ ((unused))void *param2, int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; int32_t temp; temp=p1->format[streamNo]; return temp; } static int32_t get_hfr2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->hfr; return temp; } static int32_t get_fd2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->fd; return temp; } static int32_t get_tnr2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, __attribute__ ((unused))int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->tnr; return temp; } static int32_t get_sensorW2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->sensorW[sensorNo]; return temp; } static int32_t get_sensorH2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->sensorH[sensorNo]; return temp; } static int32_t get_sensorC2( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->sensorClk[sensorNo]; return temp; } static int32_t get_sensorMP( __attribute__ ((unused))void *param1, void *param2, __attribute__ ((unused))int32_t streamNo, int32_t sensorNo ){ meta2 *p2 = (meta2 *)param2; int32_t temp; temp=p2->sensorMP[sensorNo]; return temp; } // static int32_t checkFSM( // void *param1, // void *param2, // int32_t *streamNo, // paramPtr paramType, // funcPtr func, // int32_t cmpValue, // int32_t loop, // int32_t sensorNo // ){ // uint32_t i; // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // if (loop) { // /* Search through all the streams and find the stream which matches // * After finding the match return the streamNo. */ // for (i = 0; i < p1->num_streams; i++) { // if(paramType(func,param1,param2,cmpValue,i)){ // /* Update the streamNo, to be used for further FSM indexing */ // streamNo=i; // return true; // } // } // /* No match of th cmpValue with any of the params */ // streamNo=MAX_NUM_STREAMS; // return false; // } else { // /* Don't change the streamNo */ // return paramType(func,param1,param2,cmpValue,streamNo); // } // } /********* FSM Version 2 *****************/ /****** End of FSM Version 2 *************/ // static int get_type(void *param1,__attribute__ ((unused))void *param2,int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t type=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // if(streamno == MAX_FROM_STREAMS){ // /* Not a particular stream, but the max value from all the streams */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->type[i]; // /* Get the max value */ // if(temp > type){ // type=temp; // } // } // } else { // /* From a particular stream */ // type=p1->type[streamno]; // } // /* Get param2 info */ // return type; // } // static int get_istype(void *param1,__attribute__ ((unused))void *param2,int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t istype=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // if(streamno == MAX_FROM_STREAMS){ // /* Not a particular stream, but the max value from all the streams */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->is_type[i]; // /* Get the max value */ // if(temp > istype){ // istype=temp; // } // } // } else { // /* From a particular stream */ // istype=p1->is_type[streamno]; // } // /* Get param2 info */ // return istype; // } // static int32_t get_width(void *param1,__attribute__ ((unused))void *param2,int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t width=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // if(streamno == MAX_FROM_STREAMS){ // /* Not a particular stream, but the max value from all the streams */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->stream_sizes[i].width; // /* Get the max value */ // if(temp > width){ // width=temp; // } // } // } else { // /* From a particular stream */ // width=p1->stream_sizes[streamno].width; // } // /* Get param2 info */ // return width; // } // static int32_t get_format(void *param1,__attribute__ ((unused))void *param2,int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t format=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // if(streamno == MAX_FROM_STREAMS){ // /* Not a particular stream, but the max value from all the streams */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->format[i]; // /* Get the max value */ // if(temp > format){ // format=temp; // } // } // } else { // /* From a particular stream */ // format=p1->format[streamno]; // } // /* Get param2 info */ // return format; // } // static int32_t get_height(void *param1,__attribute__ ((unused))void *param2,int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t height=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // if(streamno == MAX_FROM_STREAMS){ // /* Not a particular stream, but the max value from all the streams */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->stream_sizes[i].height; // /* Get the max value */ // if(temp > height){ // height=temp; // } // } // } else { // /* From a particular stream */ // height=p1->stream_sizes[streamno].height; // } // /* Get param2 info */ // return height; // } // static int32_t get_EIS30(void *param1,__attribute__ ((unused))void *param2,__attribute__ ((unused))int32_t streamno) { // cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; // int32_t istype=0; // int32_t temp; // uint32_t i; // /* Get param1 info */ // for (i = 0; i < p1->num_streams; i++) { // temp=p1->is_type[i]; // /* Get the max value */ // if(temp > istype){ // istype=temp; // } // } // if(istype == IS_TYPE_EIS_3_0){ // return true; // } // return false; // } // static int32_t get_hfr(__attribute__ ((unused))void *param1,void *param2,__attribute__ ((unused))int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->hfr; // } // static int32_t get_tnr(__attribute__ ((unused))void *param1,void *param2,__attribute__ ((unused))int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->tnr; // } // static int32_t get_fd(__attribute__ ((unused))void *param1,void *param2,__attribute__ ((unused))int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->fd; // } // static int32_t get_sensorW(__attribute__ ((unused))void *param1,void *param2,int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->sensorW[streamno]; // } // static int32_t get_sensorH(__attribute__ ((unused))void *param1,void *param2,int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->sensorH[streamno]; // } // static int32_t get_sensorC(__attribute__ ((unused))void *param1,void *param2,int32_t streamno) { // meta2 *p2 = (meta2 *)param2; // return p2->sensorClk[streamno]; // } static paramPtr get_param_type(char *type, int32_t *streamno,int32_t *loop){ char stream_check[7]={'\0'}; char *token; char *rest=type; int32_t no_of_tokens=0; char paramToken[10]; *streamno=MAX_FROM_STREAMS; strncpy(stream_check,type,6); if((!strcmp(stream_check,"stream")) || (!strcmp(stream_check,"sensor"))){ /* Stream specific command */ while ((token = strtok_r(rest, "_", &rest))) { if(!no_of_tokens){ /* First token is stream */ } else if (no_of_tokens == 1){ /* Second token is the command */ strcpy(paramToken,token); } else if (no_of_tokens == 2){ /* Final token is the stream no */ *streamno=atoi(token); } no_of_tokens++; } } else { LOGE("ERROR: function should start with stream_*\n"); } if (!strcmp(paramToken,"type")) { *loop = 1; return &get_type2; } else if (!strcmp(paramToken,"istype")) { *loop = 1; return &get_istype2; } else if (!strcmp(paramToken,"format")) { *loop = 0; return &get_format2; } else if (!strcmp(paramToken,"width")) { *loop = 0; return &get_width2; } else if (!strcmp(paramToken,"height")) { *loop = 0; return &get_height2; } else if (!strcmp(paramToken,"tnr")) { *loop = 0; return &get_tnr2; } else if (!strcmp(paramToken,"hfr")) { *loop = 0; return &get_hfr2; } else if (!strcmp(paramToken,"fd")) { *loop = 0; return &get_fd2; } else if (!strcmp(paramToken,"sensorW")) { *loop = 0; return &get_sensorW2; } else if (!strcmp(paramToken,"sensorH")) { *loop = 0; return &get_sensorH2; } else if (!strcmp(paramToken,"sensorC")) { *loop = 0; return &get_sensorC2; } else if (!strcmp(paramToken,"sensorMP")) { *loop = 0; return &get_sensorMP; } else { LOGE("The FSM table in file has errors 3\n"); exit(0); } return NULL; } /********* END - Get Params from strcut *********/ /********* Decision Tree Comparator funcs **********/ static int32_t func_le(int32_t param_value,int32_t comp_value) { return (param_value <= comp_value); } static int32_t func_lt(int32_t param_value,int32_t comp_value) { return (param_value < comp_value); } static int32_t func_ge(int32_t param_value,int32_t comp_value) { return (param_value >= comp_value); } static int32_t func_gt(int32_t param_value,int32_t comp_value) { return (param_value > comp_value); } static int32_t func_eq(int32_t param_value,int32_t comp_value) { return (param_value == comp_value); } static funcPtr get_func_type(char *type){ if (!strcmp("LE",type)) return &func_le; else if(!strcmp("LT",type)) return &func_lt; else if(!strcmp("GT",type)) return &func_gt; else if(!strcmp("GE",type)) return &func_ge; else if(!strcmp("EQ",type)) return &func_eq; else { LOGE("The FSM table in file has errors 2\n"); exit(0); } return NULL; } /********* END - Decision Tree Comparator funcs **********/ static uint8_t setupFSMDB(FSMDB_t *FSM,char *line){ FSMStates state=FUNC_RETURN; int32_t stream_no=MAX_FROM_STREAMS; char * token; char * rest=line; int32_t value; int32_t loop; static int32_t FSMrowCount=0; while ((token = strtok_r(rest, ":", &rest))) { //LOGE("Token : %s\n", token); switch(state){ case FUNC_RETURN: /* * FUNC = 0 * RETURN = 1 */ value = atoi(token); if(value > 1){ LOGE("The FSM table in file has errors 1\n"); return false; } FSM->FSMDB_FUNC_RETURN[FSMrowCount]=value; if (!value) { state=PARAM_TYPE; } else { state=RETURN_TYPE; } break; case RETURN_TYPE: value = atoi(token); FSM->FSMDB_RETURN[FSMrowCount]=value; break; case PARAM_TYPE: FSM->FSMDB_PARAM_TYPE[FSMrowCount]=get_param_type(token,&stream_no,&loop); FSM->FSMDB_PARAM_STREAMNO[FSMrowCount]=stream_no; FSM->FSMDB_FUNC_TYPE_LOOP[FSMrowCount]=loop; state=FUNC_TYPE; break; case FUNC_TYPE: FSM->FSMDB_FUNC_TYPE[FSMrowCount]=get_func_type(token); state=CMP_VALUE; break; case CMP_VALUE: /* Compare Value will be float */ value = atoi(token); FSM->FSMDB_CMP_VALUE[FSMrowCount]=value; state=TRUE_VALUE; break; case TRUE_VALUE: /* True value is index - hence integer */ value = atoi(token); FSM->FSMDB_TRUE_VALUE[FSMrowCount]=value; state=FALSE_VALUE; break; case FALSE_VALUE: /* False value is index - hence integer */ value = atoi(token); FSM->FSMDB_FALSE_VALUE[FSMrowCount]=value; break; } } FSMrowCount++; if (FSMrowCount>=1) return true; else return false; } static int32_t runFSM(FSMDB_t *FSM,int index,void *param1, void* param2,int32_t session,int32_t streamNo){ /* * Extract data from the structures and crank it through the FSM. */ int32_t isReturn; paramPtr param; funcPtr func; int32_t cmpValue; int32_t trueValue; int32_t falseValue; int32_t sensorNo; int32_t result; int32_t loop; uint32_t i; cam_stream_size_info_t *p1 = (cam_stream_size_info_t *)param1; isReturn=FSM->FSMDB_FUNC_RETURN[index]; if(!isReturn){ param=FSM->FSMDB_PARAM_TYPE[index]; /* Get params from the structures */ sensorNo=FSM->FSMDB_PARAM_STREAMNO[index]; func=FSM->FSMDB_FUNC_TYPE[index]; cmpValue=FSM->FSMDB_CMP_VALUE[index]; trueValue=FSM->FSMDB_TRUE_VALUE[index]; falseValue=FSM->FSMDB_FALSE_VALUE[index]; /* Compare the paramValue with cmpValue and based * on the truth value get the index to the next node */ loop = FSM->FSMDB_FUNC_TYPE_LOOP[index]; //streamNo=MAX_NUM_STREAMS; result=false; if (loop) { /* Search through all the streams and find the stream which matches * After finding the match return the streamNo. */ for (i = 0; i < p1->num_streams; i++) { if(func(param(param1,param2,i,sensorNo),cmpValue)){ /* Update the streamNo, to be used for further FSM indexing */ streamNo=i; result=true; break; } } /* No match of th cmpValue with any of the params */ } else { /* Don't change the streamNo */ result= func(param(param1,param2,streamNo,sensorNo),cmpValue); } index = (result?trueValue:falseValue); LOGH("FSM : Param = %p,StrNo = %d, func= %p, cmp= %d, true = %d, false = %d :: index = %d\n",param,streamNo,func,cmpValue,trueValue,falseValue,index); return runFSM(FSM,index,param1,param2,session,streamNo); }else{ return FSM->FSMDB_RETURN[index]; } } struct FSMDB* setupFSM(char *fileName){ // 0 - Function execute it // 1 - Return the value /* | Func/Return | ParamType | FuncType | CmpValue | TrueValue | FalseValue * | | | | | | */ FILE *fp; char table[LINESIZE]; int filesizecheck=false; if(FSM){ /* The FSM has already been setup-> don't create again. * This can happend for Dual camera */ return FSM; } FSM=(FSMDB_t *)malloc(sizeof(FSMDB_t)); if(!FSM){ LOGE("Unable to setup the FSM\n"); return NULL; } if (!(fp=(FILE *)fopen(fileName,"r"))){ LOGE("Unable to open file\n"); return NULL; } while(readlines(fp,(char *)table)){ LOGH("The node is : %s\n",table); if(!setupFSMDB(FSM,table)){ LOGE("Failed to setup FSMDB from the config file\n"); free(FSM); FSM=NULL; break; } filesizecheck=true; // TODO - error handling - file wrong errors } if(!filesizecheck) { LOGE("Failed to setup FSMDB from the config file\n"); free(FSM); FSM=NULL; } fclose(fp); return FSM; } int32_t predictFSM(FSMDB_t *FSM,void *param1, void *param2,int32_t session){ static meta2_t metaData={{0,0},{0,0},{0,0},{0,0},0,0,0}; static int32_t perfLevel[2]={0,0}; int32_t totalPerfLevel=0,prevPerfLevel; cam_perf_info_t *meta=(cam_perf_info_t *)param2; // returning value 0 means no-throttling // IMP: Not tracking no of sessions. if(!FSM){ LOGE("Unable to predict as NULL FSM passed\n"); return 0; } prevPerfLevel=perfLevel[0]+perfLevel[1]; // 1. struct1 & struct2 = NULL indicates session_no is getting closed if ((NULL == (int32_t *)param1) || (NULL == (int32_t *)param2)) { // Either structs is NULL, then the session_no is de-activated perfLevel[session]=0; } else if( (meta->sensorW * meta->sensorH) == 0) { // invalid case if either sensorW or sensorH is 0 // sensor values should always be updated perfLevel[session]=0; } else { // 2. Based on Session No compute the perf level metaData.sensorW[session]=meta->sensorW; metaData.sensorH[session]=meta->sensorH; metaData.sensorClk[session]=meta->sensorClk; metaData.sensorMP[session]=((meta->sensorW)*(meta->sensorH))/(1024*1024); metaData.hfr=meta->hfr; metaData.fd=meta->fd; metaData.tnr=meta->tnr; perfLevel[session]=runFSM(FSM,0,param1,&metaData,session,8); } // 3. Add the perf levels everytime for all sessions totalPerfLevel=perfLevel[0]+perfLevel[1]; totalPerfLevel=(totalPerfLevel>FSM_MAX_LEVEL?FSM_MAX_LEVEL:totalPerfLevel); // Return the total perf level based on no of sessions LOGH("The predicted value from FSM : %d\n",totalPerfLevel); if (prevPerfLevel > totalPerfLevel) { // First send 0 then totalPerfLevel LOGH("Prev Level greater than New Level\n"); } else if (prevPerfLevel == totalPerfLevel) { // no need to send LOGH("Prev Level same as New Level\n"); } else { // send LOGH("Prev Level smaller than New Level\n"); } return totalPerfLevel; } void closeFSM(){ if (FSM) { free(FSM); FSM = NULL; } }