mtk-lk display代码分析

一.mtk显示架构

分析代码时会看到ddp-xxx开头的文件,ddp指的是display data path,下面是6582的ddp结构,比较老的芯片,新的没找到,但是大体流程应该是类似的。

  • 1.rotator

  图像旋转,支持多个方向旋转,也支持局部旋转

  • 2.scaler

  图像缩放,水平垂直方向缩放

  • 3.color engine

  图像颜色处理,PQ处理,改变亮度色坐标等

  • 4.OVL(overlayer)

  数据输入:direct link scaler format,YUV or RGB;memory source format.可以直接从上级模块SCL or PQ,也可以直接从memory输入

  支持4组layer:支持ROI(region of interest),自定义各layer的大小、起始地址、显示区域

  支持数据重新map:例如RGB、BGR之间转换

  支持数据类型转换:;例如YUV转RGB

  • 5.WDMA(write DMA)

  模块负责数据写入DRAM中,OVL-->WDMA1实现截图function

扫描二维码关注公众号,回复: 1515249 查看本文章
  • 6.RDMA(read DMA)

  模块负责从DRAM中将数据写入显示模块,如DSI、DPI、DBI(这些都是MIPI显示接口类型)

  RDMA0支持两种输入,direct link和memory input;RDMA1只支持memory input

  • 7.BLS(背光相关)

  通过pwm等调节背光,根据图片显示内容改变背光亮度(CABC)

二.display一些基本概念

1.video mode & command mode

  •   video mode:显示数据流通过driver IC直接显示到lcd上,为实时数据
  •   command mode:数据先更新到ram中再由ram刷新到lcd上。<1>这种模式需要ic带ram,平台无数据更新时,显示内容就由ram更新到lcd上,节省功耗。<2>1/2 or 1/3 ram(ram大小为一帧的1/2 1/3)可以实现平台较小的数据输入,输出较高分辨率的效果,平台方压缩显示数据,driver IC解压数据并显示到lcd上。

2.DSI、DBI、DPI

  •   DSI,串行接口,实现较高的数据传输
  •   DPI,并行接口,实时传输
  •   DBI,并行接口,driver IC带ram

3.video mode的类型

  

  •      Non-Burst Mode with Sync Pulses :传输时包括sync pulse

  •   Non-Burst Mode with Sync Events :相比上一种只是普通的同步事件

  •   Burst mode  :在scan line传输完RGB信号其余时间均为LP11模式

4.MIPI DSI接口PLL计算

  总数据量=(VS+VBP+VACT+VFP)*(HS+HBP+HACT+HFP)* fps * format_bit(format_bit根据一个pixel的数据位来定,大多为24位,RGB888)

  每lane的数据量=总数据量/lane数

  DSI为差分信号,一个clock内双沿采样,传输2bit数据

  则最终PLL速率为每lane数据量/2

  

三.lk中lcm添加新的驱动

  • 1.在/dev/lcm/mtxx_lcm_list中包含这样一个结构体数组,数据定义了各种lcm设备。例如标红的是新增的lcm。
LCM_DRIVER *lcm_driver_list[] = {
#if defined(NT35595_FHD_DSI_CMD_TRULY_8163) &nt35595_fhd_dsi_cmd_truly_8163_lcm_drv, #endif #if defined(OTM1284A_HD720_DSI_VDO_TM) &otm1284a_hd720_dsi_vdo_tm_lcm_drv, #endif #if defined(R69338_FHD_DSI_VDO_JDI) &r69338_fhd_dsi_vdo_jdi_drv, #endif #if defined(FT8707_FHD_DSI_VDO_LGD) &ft8707_fhd_dsi_vdo_lgd_drv, #endif #if defined(OTM1285A_HD720_DSI_VDO_TM) &otm1285a_hd720_dsi_vdo_tm_lcm_drv, #endif ...... }
  • 2.添加nt35595的驱动代码,截取LCM_DRIVER这部分代码
LCM_DRIVER nt35595_fhd_dsi_cmd_truly_8163_lcm_drv= {
    .name               = "nt35595_fhd_dsi_cmd_truly_8163",
    .set_util_funcs     = lcm_set_util_funcs,
    .get_params         = lcm_get_params,
    .init               = lcm_init,
    .suspend            = lcm_suspend,
    .resume             = lcm_resume,
    .init_power         = lcm_init_power,
    .resume_power       = lcm_resume_power,
    .suspend_power      = lcm_suspend_power,
#if (LCM_DSI_CMD_MODE)
    .update             = lcm_update,
#endif

};
  • 3.在/project/xx.mk中添加新的lcm,会根据name选取对应的驱动进行初始化操作
CUSTOM_LK_LCM="nt35595_fhd_dsi_cmd_truly_8163"

四.lk显示代码分析

  • 1.platform_init代码分析
//framebuffer的大小
g_fb_size = mt_disp_get_vram_size();
//framebuffer的起始地址 g_fb_base
= mblock_reserve(&g_boot_arg->mblock_info, g_fb_size, 0x10000, 0xa0000000, RANKMAX);
//mtk display系统初始化 mt_disp_init((
void *)g_fb_base);
//fb预先填充black mt_disp_fill_rect(
0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT, 0x0);
//将fb内容更新到lcm上 mt_disp_update(
0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);
//加载logo mboot_common_load_logo((unsigned
long)mt_get_logo_db_addr_pa(), "logo");
//根据启动方式选择加载的logo,填充到fb中 mt_disp_show_boot_logo();
//打开背光 mt65xx_backlight_on(); mt_disp_update(
0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);
  • 2.mt_disp_get_vram_size,首先看下如下三个结构体
//LCM_DRIVER,lcm相关的一系列的操作函数
typedef struct { const char *name;  --用于匹配lcm void (*set_util_funcs) (const LCM_UTIL_FUNCS *util); --设置lcm操作函数,gpio、reset、delay、write/read cmd void (*get_params) (LCM_PARAMS *params);  --获取lcm的参数,接口、分辨率、porch、PLL clock、data format等 void (*init) (void);  --lcm初始化,一般为上电复位初始化 void (*suspend) (void);  --pm相关 void (*resume) (void); /* for power-on sequence refinement */ void (*init_power) (void);  --上电初始化 void (*suspend_power) (void); void (*resume_power) (void); void (*update) (unsigned int x, unsigned int y, unsigned int width, unsigned int height);  --设置ram的显示区域(x,y)->(x+width,y+height),只在command mode下使用 unsigned int (*compare_id) (void); void (*parse_dts)(const LCM_DTS *DTS, unsigned char force_update);  --解析设备树 /* /////////////////////////CABC backlight related function */ void (*set_backlight) (unsigned int level);  --背光相关函数 void (*set_backlight_cmdq) (void *handle, unsigned int level); void (*set_pwm) (unsigned int divider); unsigned int (*get_pwm) (unsigned int divider); void (*set_backlight_mode) (unsigned int mode); /* ///////////////////////// */ int (*adjust_fps) (void *cmdq, int fps);  --调节刷新率,一般刷新率为60Hz /* ///////////ESD_RECOVERY////////////////////// */ unsigned int (*esd_check) (void);  --ESD检测相关,开启该功能,系统会周期性检测lcm寄存器状态,状态NG则会reset lcm unsigned int (*esd_recover) (void); unsigned int (*check_status) (void); unsigned int (*ata_check) (unsigned char *buffer); void (*read_fb) (unsigned char *buffer); int (*ioctl) (LCM_DRV_IOCTL_CMD cmd, unsigned int data); /* /////////////////////////////////////////////// */ void (*enter_idle)(void); void (*exit_idle)(void); void (*change_fps)(unsigned int mode); /* //switch mode */
   void *(*switch_mode) (int mode);  --用于切换mode,command mode 和video mode切换 void (*set_cmd) (void *handle, int *mode, unsigned int cmd_num); void (*set_lcm_cmd) (void *handle, unsigned int *lcm_cmd, unsigned int *lcm_count, unsigned int *lcm_value); /* /////////////PWM///////////////////////////// */ void (*set_pwm_for_mix) (int enable); } LCM_DRIVER;
//LCM_PARAMS,lcm参数设置
typedef struct {
    LCM_TYPE type;  --lcm的接口类型,分为DSI、DBI、DPI        
    LCM_CTRL ctrl;   --lcm寄存器的访问方式,普遍通过MIPI LP下指令,也有的支持spi/i2c等接口访问   
    LCM_INTERFACE_ID lcm_if;  --lcm的接口id,分为DSI0、DSI1、dual port DSI、DBI0、DPI0、DPI1
    LCM_INTERFACE_ID lcm_cmd_if;  --cmd模式接口,和lcm_if一致
    /* common parameters */
    unsigned int lcm_x;  --(lcm_x,lcm_y)定义显示的起始pixel,(virtual_width,virtual_height)or(width,height)定义显示区域的宽和高
    unsigned int lcm_y;  
    unsigned int width;
    unsigned int height;
    unsigned int virtual_width;  
    unsigned int virtual_height;
    unsigned int io_select_mode;    /* DBI or DPI should select IO mode according to chip spec */

    /* particular parameters */
    LCM_DBI_PARAMS dbi;  --DBI参数
    LCM_DPI_PARAMS dpi;  --DPI参数
    LCM_DSI_PARAMS dsi;  --DSI参数
    unsigned int physical_width;
    unsigned int physical_height;
    unsigned int od_table_size;
    void *od_table;  --光学相关表格,gamma节点
} LCM_PARAMS;
typedef struct {
    LCM_PARAMS          *params;  
    LCM_DRIVER          *drv;  
    LCM_INTERFACE_ID    lcm_if_id;  --接口id    
    int                 module;
    int                 is_inited;  --lcm是否初始化
    int                 is_connected;  --lcm是否连接
} disp_lcm_handle, *pdisp_lcm_handle;
  • 3.代码分析
    • 找到匹配的lcm并进行disp_lcm_handle结构体初始化
 mt_disp_get_vram_size();
    -->DISP_GetVRamSize();
        -->DISP_GetFBRamSize();
            -->DISP_GetScreenWidth();
                -->primary_display_get_width();
            -->disp_lcm_probe(NULL, LCM_INTERFACE_NOTDEFINED);
//disp_lcm_probe,根据lcm_driver_list找到匹配的lcm,实现plcm结构体初始化
disp_lcm_handle* disp_lcm_probe(char* plcm_name, LCM_INTERFACE_ID lcm_id) { if (_lcm_count() == 0) { DISPERR("no lcm driver defined in linux kernel driver\n"); return NULL; } else if (_lcm_count() == 1) { lcm_drv = lcm_driver_list[0];    --这里只连接一个lcm isLCMFound = true; } else { ...... } plcm = &_disp_lcm_driver[0]; lcm_param = &_disp_lcm_params; if (plcm && lcm_param) { plcm->params = lcm_param; plcm->drv = lcm_drv;
  } plcm
->drv->get_params(plcm->params);  --执行get_params函数,初始化plcm->params plcm->lcm_if_id = plcm->params->lcm_if;  --接口id }
 
//其中一颗FHD IC的驱动参数
static void lcm_get_params(LCM_PARAMS * params)
{
    memset(params, 0, sizeof(LCM_PARAMS));

    params->type = LCM_TYPE_DSI;  --接口类型,还可以是DBI、DPI

    params->width = FRAME_WIDTH;  --显示宽度
    params->height = FRAME_HEIGHT;  --显示高度

    // enable tearing-free
    params->dbi.te_mode = LCM_DBI_TE_MODE_DISABLED;  --TE模式disable
    params->dbi.te_edge_polarity = LCM_POLARITY_RISING;  --DBI相关,这里表示上升沿采样

     #if (LCM_DSI_CMD_MODE)
     params->dsi.mode   = CMD_MODE;
   #else
     params->dsi.mode   = SYNC_PULSE_VDO_MODE;//BURST_VDO_MODE;  --video mode
   #endif
    // DSI
    /* Command mode setting */
    params->dsi.LANE_NUM = LCM_FOUR_LANE;  --mipi four lane//The following defined the fomat for data coming from LCD engine.
    params->dsi.data_format.color_order = LCM_COLOR_ORDER_RGB;  --pixel的color顺序,这里是RGB,有些panel pixel是BGR排列
    params->dsi.data_format.trans_seq = LCM_DSI_TRANS_SEQ_MSB_FIRST;  --高位先传输
    params->dsi.data_format.padding = LCM_DSI_PADDING_ON_LSB;  --
    params->dsi.data_format.format = LCM_DSI_FORMAT_RGB888;  --RGB888的排列

    // Highly depends on LCD driver capability.
    params->dsi.packet_size = 256;  --包的最大byte数

    // Video mode setting    
    params->dsi.PS = LCM_PACKED_PS_24BIT_RGB888;  --24bit RGB888排列
    params->dsi.vertical_sync_active = 3;  --vs,porch设定,需要满足driver IC spec的要求
    params->dsi.vertical_backporch = 5;  --vbp
    params->dsi.vertical_frontporch = 8;  --vfp
    params->dsi.vertical_active_line = FRAME_HEIGHT;  --vact 

    params->dsi.horizontal_sync_active = 50;  --hs
    params->dsi.horizontal_backporch = 100;  --hbp
    params->dsi.horizontal_frontporch = 100;  --hfp
    params->dsi.horizontal_active_pixel = FRAME_WIDTH;  --hact

    params->dsi.PLL_CLOCK = 481;  --mipi速率
}
    • ddp中各个模块和lcm配置初始化  
        • <1>创建显示路径(display data path   
//定义了支持的ddp num,即mtk支持这么多种显示方案,每种显示方案对应由不同的模块组成,构成一个二维数组,module_list_scenario
typedef enum {
    DDP_SCENARIO_PRIMARY_DISP = 0,
    DDP_SCENARIO_PRIMARY_RDMA0_COLOR0_DISP,
    DDP_SCENARIO_PRIMARY_RDMA0_DISP,
    DDP_SCENARIO_PRIMARY_BYPASS_RDMA,
    DDP_SCENARIO_PRIMARY_OVL_MEMOUT,
    DDP_SCENARIO_PRIMARY_DITHER_MEMOUT,
    DDP_SCENARIO_PRIMARY_UFOE_MEMOUT,
    DDP_SCENARIO_DISPLAY_INTERFACE,
    DDP_SCENARIO_PRIMARY_ALL,
    DDP_SCENARIO_SUB_DISP,
    DDP_SCENARIO_SUB_RDMA1_DISP,
    DDP_SCENARIO_SUB_OVL_MEMOUT,
    DDP_SCENARIO_SUB_ALL,
    DDP_SCENARIO_MAX
} DDP_SCENARIO_ENUM;

int module_list_scenario[DDP_SCENARIO_MAX][DDP_ENING_NUM] = {
  /*PRIMARY_DISP*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_RDMA0, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},
  /*PRIMARY_RDMA0_COLOR0_DISP*/ {DISP_MODULE_RDMA0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER,DISP_MODULE_UFOE, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},
  /*PRIMARY_RDMA0_DISP*/ {DISP_MODULE_RDMA0, DISP_MODULE_DSI0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
  /*PRIMARY_BYPASS_RDMA*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_UFOE, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},
  /*PRIMARY_OVL_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
  /*PRIMARY_DITHER_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1},
  /*PRIMARY_UFOE_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR,DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_RDMA0, DISP_MODULE_UFOE, DISP_MODULE_WDMA0,-1, -1, -1},
  /*SUB_DISP*/ {DISP_MODULE_OVL1, DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1,-1,-1,-1,-1,-1,-1,-1},
  /*SUB_RDMA1_DISP*/ {DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
  /*SUB_OVL_MEMOUT*/ {DISP_MODULE_OVL1, DISP_MODULE_WDMA1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
  /*PRIMARY_ALL*/ {DISP_MODULE_OVL0, DISP_MODULE_WDMA0, DISP_MODULE_COLOR0,DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER,DISP_MODULE_RDMA0,DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1},
  /*SUB_ALL*/ {DISP_MODULE_OVL1, DISP_MODULE_WDMA1, DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1,-1, -1,-1,-1,-1, -1,-1},
  /*MULTIPLE_OVL*/ {DISP_MODULE_OVL1, DISP_MODULE_OVL0, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1, -1,-1,-1,-1},
};

//pgc结构体

typedef struct {
  int state;
  int need_trigger_overlay;
  DISP_PRIMARY_PATH_MODE mode;
  unsigned int last_vsync_tick;
  disp_lcm_handle * plcm;
  cmdqRecHandle cmdq_handle_config;
  cmdqRecHandle cmdq_handle_trigger;
  disp_path_handle dpmgr_handle;
  disp_path_handle ovl2mem_path_handle;
} display_primary_path_context;

//ddp_path_handle结构体

typedef struct {
  cmdqRecHandle cmdqhandle;
  int hwmutexid;
  int power_sate;
  DDP_MODE mode;
  //struct mutex mutex_lock;
  DDP_IRQ_EVENT_MAPPING irq_event_map[DISP_PATH_EVENT_NUM];
  DPMGR_WQ_HANDLE wq_list[DISP_PATH_EVENT_NUM];
  DDP_SCENARIO_ENUM scenario;
  disp_ddp_path_config last_config;
} ddp_path_handle_t, *ddp_path_handle;

        • int primary_display_init(char *lcm_name)
          {
                  ......

                    pgc->plcm = disp_lcm_probe( lcm_name, LCM_INTERFACE_NOTDEFINED);

          if (primary_display_mode == DIRECT_LINK_MODE) {
                  _build_path_direct_link();    
                  ......
          }
          static int _build_path_direct_link(void)
          {
              DISP_MODULE_ENUM dst_module = 0;
              pgc->mode = DIRECT_LINK_MODE;
          
              pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_DISP, pgc->cmdq_handle_config);  --根据显示方案设置ddp_path_handle
              dst_module = _get_dst_module_by_lcm(pgc->plcm);  --根据lcm接口类型获得目标模块,这里为DISP_MODULE_DSI0
              dpmgr_path_set_dst_module(pgc->dpmgr_handle, dst_module);    --设置ddp的最后一个模块为DISP_MODULE_DSI0
          dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv); --设置各个模块中lcm相关的操作函数,调用ddp_dsi_set_lcm_utils,最终调用lcm_drv->set_util_funcs(utils)将操作函数赋给lcm_util
          }

          disp_path_handle dpmgr_create_path(DDP_SCENARIO_ENUM scenario, cmdqRecHandle cmdq_handle)
          {
              int i =0;
              int module_name ;
              ddp_path_handle path_handle = NULL;
              int * modules = ddp_get_scenario_list(scenario);
              int module_num = ddp_get_module_num(scenario);
              DDP_MANAGER_CONTEXT * content = _get_context();
              //path_handle = kzalloc(sizeof(uint8_t*) * sizeof(ddp_path_handle_t), GFP_KERNEL);
              memset((void*)(&g_handle), 0, sizeof(ddp_path_handle_t));
              path_handle = &g_handle;
              if (NULL != path_handle) {
                  path_handle->cmdqhandle = cmdq_handle;
                  path_handle->scenario = scenario;
                  path_handle->hwmutexid = acquire_mutex(scenario);
                  assign_default_irqs_table(scenario,path_handle->irq_event_map);
                  DISP_LOG_I("create handle 0x%p on scenario %s\n",path_handle,ddp_get_scenario_name(scenario));
                  for ( i=0; i< module_num; i++) {
                     module_name = modules[i];
                     DISP_LOG_V(" scenario %s include module %s\n",ddp_get_scenario_name(scenario),ddp_get_module_name(module_name));
                     content->module_usage_table[module_name]++;
                     content->module_path_table[module_name] = path_handle;
                  }
              content->handle_cnt ++;
              content->handle_pool[path_handle->hwmutexid] = path_handle;
              } else {
                  DISP_LOG_E("Fail to create handle on scenario %s\n",ddp_get_scenario_name(scenario));
              }
              return path_handle;
          }

           
      • <2>ddp设置显示模式,video mode or command mode
        dpmgr_path_set_video_mode(pgc->dpmgr_handle, primary_display_is_video_mode());
        int dpmgr_path_set_video_mode(disp_path_handle dp_handle, int is_vdo_mode)
        {
            ddp_path_handle handle = NULL;
            ASSERT(dp_handle != NULL);
            handle = (ddp_path_handle)dp_handle;
            handle->mode = is_vdo_mode ?  DDP_VIDEO_MODE : DDP_CMD_MODE;
            DISP_LOG_I("set scenario %s mode %s\n",ddp_get_scenario_name(handle->scenario),
                       is_vdo_mode ? "Video Mode":"Cmd Mode");
            return 0;
        }
      • <3>ddp上各个模块的初始化,ovl0、ovl1、rdma0、rdma1、color、aal、gamma、dither、dsi0
        dpmgr_path_init(pgc->dpmgr_handle, CMDQ_DISABLE);
        //调用各个模块的init函数
        int
        dpmgr_path_init(disp_path_handle dp_handle, int encmdq) { ddp_path_handle handle = (ddp_path_handle)dp_handle; int * modules = ddp_get_scenario_list(handle->scenario); int module_num = ddp_get_module_num(handle->scenario); cmdqRecHandle cmdqHandle = encmdq ? handle->cmdqhandle : NULL;//turn off m4u ddp_path_m4u_off(); //open top clock ddp_path_top_clock_on(); //seting mutex ddp_mutex_set(handle->hwmutexid, handle->scenario, handle->mode, cmdqHandle); //connect path; ddp_connect_path(handle->scenario,cmdqHandle); // each module init for ( i=0; i< module_num; i++) { module_name = modules[i]; if (ddp_modules_driver[module_name] != 0) { if (ddp_modules_driver[module_name]->init!= 0) { ddp_modules_driver[module_name]->init(module_name, cmdqHandle); } if (ddp_modules_driver[module_name]->set_listener!= 0) { ddp_modules_driver[module_name]->set_listener(module_name,dpmgr_module_notify); } } } //after init this path will power on; handle->power_sate = 1; return 0; }
      • <4>ddp上各个模块的配置
        //data_config结构体描述lcm分辨率、接口类型、数据排列等信息
          disp_ddp_path_config data_config; memset((
        void*)&data_config, 0, sizeof(disp_ddp_path_config)); //这里只考虑DSI接口 memcpy(&(data_config.dsi_config), &(lcm_param->dsi), sizeof(LCM_DSI_PARAMS)); data_config.dst_w = disp_lcm_width(pgc->plcm); data_config.dst_h = disp_lcm_height(pgc->plcm); if (lcm_param->type == LCM_TYPE_DSI) { if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB888) data_config.lcm_bpp = 24; else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB565) data_config.lcm_bpp = 16; else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB666) data_config.lcm_bpp = 18; } else if (lcm_param->type == LCM_TYPE_DPI) { if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB888) data_config.lcm_bpp = 24; else if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB565) data_config.lcm_bpp = 16; if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB666) data_config.lcm_bpp = 18; } data_config.dst_dirty = 1; ret = dpmgr_path_config(pgc->dpmgr_handle, &data_config, CMDQ_DISABLE);
        //调用各个模块的config函数
        int
        dpmgr_path_config(disp_path_handle dp_handle, disp_ddp_path_config * config, int encmdq) { int i=0; int module_name ; ASSERT(dp_handle != NULL); ddp_path_handle handle = (ddp_path_handle)dp_handle; int * modules = ddp_get_scenario_list(handle->scenario); int module_num = ddp_get_module_num(handle->scenario); cmdqRecHandle cmdqHandle = encmdq ? handle->cmdqhandle : NULL; memcpy(&handle->last_config, config, sizeof(disp_ddp_path_config)); for ( i=0; i< module_num; i++) { module_name = modules[i]; if (ddp_modules_driver[module_name] != 0) { if (ddp_modules_driver[module_name]->config!= 0) { ddp_modules_driver[module_name]->config(module_name, config, cmdqHandle); } } } return 0; }
      • <5>lcm初始化,通过回读0x0A寄存器判断lcm是否连接
        //依次调用lcm的init_power和init函数,实现上电复位初始化的动作,并通过回读0x0A寄存器判断跟lcm之前是否连接OK
        int
        disp_lcm_init(disp_lcm_handle *plcm) { LCM_DRIVER *lcm_drv = NULL; bool isLCMConnected = false; if (_is_lcm_inited(plcm)) { lcm_drv = plcm->drv; if (lcm_drv->init_power) { lcm_drv->init_power(); } if (lcm_drv->init) { if (!disp_lcm_is_inited(plcm)) { lcm_drv->init(); } } else {return -1; } if (LCM_TYPE_DSI == plcm->params->type) { ret = DSI_dcs_read_lcm_reg_v2(_get_dst_module_by_lcm(plcm), NULL, 0x0A, (UINT8 *)&buffer,1); if (ret == 0) { isLCMConnected = 0; } else { isLCMConnected = 1; } } if (plcm->params->dsi.edp_panel == 1) { isLCMConnected = 1; } plcm->is_connected = isLCMConnected; return 0; } else { return -1; } }
      • <6>关联event与irq,并且使能event,例如一帧刷完这个event,便产生一个中断
        //映射event和中断,并且使能中断事件
        if (primary_display_is_video_mode()) { if (lcm_param->dsi.lfr_enable == 1) { dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_DSI0_FRAME_DONE); } else { dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_RDMA0_DONE); } } else { } dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC); dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE); pgc->state = 1;
        int dpmgr_enable_event(disp_path_handle dp_handle, DISP_PATH_EVENT event)
        {
            ASSERT(dp_handle != NULL);
            ddp_path_handle handle = (ddp_path_handle)dp_handle;
            DPMGR_WQ_HANDLE *wq_handle = &handle->wq_list[event];
                       ddp_get_scenario_name(handle->scenario),
                       event,
                       handle->irq_event_map[event].irq_bit);
            if (!wq_handle->init) {
                //init_waitqueue_head(&(wq_handle->wq));
                wq_handle->init = 1;
                wq_handle->data= 0;
                wq_handle->event = event;
            }
            return 0;
        }
        //mtk display支持16类事件
        typedef enum{
            DISP_PATH_EVENT_FRAME_START = 0,
            DISP_PATH_EVENT_FRAME_DONE,
            DISP_PATH_EVENT_FRAME_REG_UPDATE,
            DISP_PATH_EVENT_FRAME_TARGET_LINE,
            DISP_PATH_EVENT_FRAME_COMPLETE,
            DISP_PATH_EVENT_FRAME_STOP,
            DISP_PATH_EVENT_IF_CMD_DONE,
            DISP_PATH_EVENT_IF_VSYNC,
            DISP_PATH_EVENT_AAL_TRIGGER,
            DISP_PATH_EVENT_COLOR_TRIGGER,
            DISP_PATH_EVENT_NUM,
            DISP_PATH_EVENT_NONE = 0xff,
        }DISP_PATH_EVENT;
        //display相关的中断类型,ddp irq由模块和其包含的中断组成
        typedef enum {
            DDP_IRQ_RDMA0_REG_UPDATE =      (DISP_MODULE_RDMA0 <<16  | 0x1<<0),
            DDP_IRQ_RDMA0_START =           (DISP_MODULE_RDMA0 <<16  | 0x1<<1),
            DDP_IRQ_RDMA0_DONE  =           (DISP_MODULE_RDMA0 <<16  | 0x1<<2),
            DDP_IRQ_RDMA0_UNDERFLOW =       (DISP_MODULE_RDMA0 <<16  | 0x1<<3),
            DDP_IRQ_RDMA0_TARGET_LINE =     (DISP_MODULE_RDMA0 <<16  | 0x1<<4),
        
            DDP_IRQ_RDMA1_REG_UPDATE =      (DISP_MODULE_RDMA1 <<16  | 0x1<<0),
            DDP_IRQ_RDMA1_START =           (DISP_MODULE_RDMA1 <<16  | 0x1<<1),
            DDP_IRQ_RDMA1_DONE  =           (DISP_MODULE_RDMA1 <<16  | 0x1<<2),
            DDP_IRQ_RDMA1_UNDERFLOW =       (DISP_MODULE_RDMA1 <<16  | 0x1<<3),
            DDP_IRQ_RDMA1_TARGET_LINE =     (DISP_MODULE_RDMA1 <<16  | 0x1<<4),
        
            DDP_IRQ_RDMA2_REG_UPDATE =      (DISP_MODULE_RDMA2 <<16  | 0x1<<0),
            DDP_IRQ_RDMA2_START =           (DISP_MODULE_RDMA2 <<16  | 0x1<<1),
            DDP_IRQ_RDMA2_DONE  =           (DISP_MODULE_RDMA2 <<16  | 0x1<<2),
            DDP_IRQ_RDMA2_UNDERFLOW =       (DISP_MODULE_RDMA2 <<16  | 0x1<<3),
            DDP_IRQ_RDMA2_TARGET_LINE =     (DISP_MODULE_RDMA2 <<16  | 0x1<<4),
        
            DDP_IRQ_WDMA0_FRAME_COMPLETE =  (DISP_MODULE_WDMA0<<16   | 0x1<<0),
            DDP_IRQ_WDMA1_FRAME_COMPLETE =  (DISP_MODULE_WDMA1<<16   | 0x1<<0),
        
            DDP_IRQ_DSI0_EXT_TE =           (DISP_MODULE_DSI0 <<16  | 0x1<<2),
            DDP_IRQ_DSI0_FRAME_DONE =       (DISP_MODULE_DSI0 <<16  | 0x1<<4),
        
            DDP_IRQ_UNKNOW =                (DISP_MODULE_UNKNOWN<<16 | 0x1<<0),
        
        } DDP_IRQ_BIT;
    • 显示输入的配置--各层layer的初始化
      //lk阶段定义了2层layer(最多支持4层layer),FB_LAYER和BOOT_MENU_LAYER, 
      disp_input_config input; memset(
      &input, 0, sizeof(disp_input_config)); input.layer = BOOT_MENU_LAYER; input.layer_en = 1; input.fmt = redoffset_32bit ? eBGRA8888 : eRGBA8888; input.addr = boot_mode_addr; input.src_x = 0; input.src_y = 0; input.src_w = CFG_DISPLAY_WIDTH; input.src_h = CFG_DISPLAY_HEIGHT; input.src_pitch = CFG_DISPLAY_WIDTH*4; input.dst_x = 0; input.dst_y = 0; input.dst_w = CFG_DISPLAY_WIDTH; input.dst_h = CFG_DISPLAY_HEIGHT; input.aen = 1; input.alpha = 0xff; primary_display_config_input(&input); memset(&input, 0, sizeof(disp_input_config)); input.layer = FB_LAYER; input.layer_en = 1; input.fmt = redoffset_32bit ? eBGRA8888 : eRGBA8888; input.addr = fb_addr_pa; input.src_x = 0; input.src_y = 0; input.src_w = CFG_DISPLAY_WIDTH; input.src_h = CFG_DISPLAY_HEIGHT; input.src_pitch = ALIGN_TO(CFG_DISPLAY_WIDTH, MTK_FB_ALIGNMENT)*4; input.dst_x = 0; input.dst_y = 0; input.dst_w = CFG_DISPLAY_WIDTH; input.dst_h = CFG_DISPLAY_HEIGHT; input.aen = 1; input.alpha = 0xff; primary_display_config_input(&input);
      int primary_display_config_input(disp_input_config* input)
      {
          // all dirty should be cleared in dpmgr_path_get_last_config()
          memcpy((void*)&data_config, (void*)dpmgr_path_get_last_config(pgc->dpmgr_handle), sizeof(disp_ddp_path_config));
          // no need do this dirty = 0; dpmgr_path_get_last_config do this.
          data_config.dst_dirty = 0;
          data_config.ovl_dirty = 0;
          data_config.rdma_dirty = 0;
          data_config.wdma_dirty = 0;
              if (pgc->mode == DIRECT_LINK_MODE || pgc->mode == DECOUPLE_MODE) {
              if (dpmgr_path_is_busy(pgc->dpmgr_handle)) {  --检查各个模块是否处于busy状态
                  if (primary_display_is_video_mode()) {
                      dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ*1);
                  }
              }
              ret = _convert_disp_input_to_ovl(&(data_config.ovl_config[input->layer]), input);  --将各层layer信息传递给overlay
              data_config.ovl_dirty = 1;
              ret = dpmgr_path_config(pgc->dpmgr_handle, &data_config, primary_display_use_cmdq);
      
              // this is used for decouple mode, to indicate whether we need to trigger ovl
              pgc->need_trigger_overlay = 1;    --设置显示需要触发overlay
          } 
      }
      disp_ddp_path_config *dpmgr_path_get_last_config(disp_path_handle dp_handle)
      {
          ddp_path_handle handle = (ddp_path_handle)dp_handle;
          handle->last_config.ovl_dirty  =0;
          handle->last_config.rdma_dirty =0;
          handle->last_config.wdma_dirty =0;
          handle->last_config.dst_dirty =0;
          return &handle->last_config;
      }

  

  

  

猜你喜欢

转载自www.cnblogs.com/ant-man/p/9139104.html
MTK