搜索

7

主题

15

帖子

171

积分

注册会员

Rank: 2

积分
171
发表于 2020-8-15 09:51:59 3548 浏览 2 回复

MTK HDMI Driver Process Analysis

本帖最后由 chenzq 于 2020-8-15 09:53 编辑

以MT8735 HDMI框架流程做为分析:
一、HDMITX虚拟平台驱动分析:
1、将mtk_extd_mgr_init外部显示管理模块加载到内核中,具体内容见函数分析。
static int __init mtk_extd_mgr_init(void)
{
         inti = 0;
         intret = 0;
/*     structnotifier_block notifier;*/
         EXT_MGR_FUNC();

         extd_driver[DEV_MHL]= EXTD_HDMI_Driver();    //获得HDMI驱动接口
         extd_driver[DEV_EINK]= EXTD_EPD_Driver();       //
         extd_factory_driver[DEV_MHL]= EXTD_Factory_HDMI_Driver();                  
         for(i = DEV_MHL; i < DEV_MAX_NUM - 1; i++) {
                   if(extd_driver->init)
                            extd_driver->init();        //初始化外部HDMI
         }
         if(platform_driver_register(&external_display_driver)) {      //注册hdmitx驱动到平台中
                   EXT_MGR_ERR("[EXTD]failedto register mtkfb driver\n");
                   return-1;
         }
         notifier.notifier_call= fb_notifier_callback;             //fb发生驱动事件的回调函数
         ret= fb_register_client(¬ifier);                   //添加到fb到通知链表中
         printk("chenzqmtk_extd_mgr_init ret=%d\n",ret);
         if(ret)
                   EXT_MGR_ERR("unableto register fb callback!\n");
#ifdef CONFIG_HAS_EARLYSUSPEND
         register_early_suspend(&extd_early_suspend_handler);
#endif
         return0;
}
2、设备与驱动匹配成功之后,调用mtk_extd_mgr_probe函数:主要是注册字符设备,绑定设备号到文件系统中,提供上层控制接口,最后轮询列表,执行hdmi的后初始化。
static int mtk_extd_mgr_probe(structplatform_device *pdev)
{
         intret = 0;
         inti = 0;
         structclass_device *class_dev = NULL;
         EXT_MGR_FUNC();
         printk("chenzqmtk_extd_mgr_probe \n");
         /*Allocate device number for hdmi driver */
         ret= alloc_chrdev_region(&extd_devno, 0, 1, EXTD_DEVNAME);
         if(ret) {
                   EXT_MGR_LOG("alloc_chrdev_regionfail\n");
                   return-1;
         }
         /*For character driver register to system, device number binded to fileoperations */
         extd_cdev= cdev_alloc();
         extd_cdev->owner= THIS_MODULE;
         extd_cdev->ops= &external_display_fops;
         ret= cdev_add(extd_cdev, extd_devno, 1);
         /*For device number binded to device name(hdmitx), one class is corresponeded to onenode */
         extd_class= class_create(THIS_MODULE, EXTD_DEVNAME);
         /*mknod /dev/hdmitx */
         class_dev= (struct class_device *)device_create(extd_class, NULL, extd_devno, NULL,EXTD_DEVNAME);
         ext_dev_context= (struct device *)&(pdev->dev);
         for(i = DEV_MHL; i < DEV_MAX_NUM - 1; i++) {
                   if(extd_driver->post_init != 0)
                            extd_driver->post_init();
         }
         EXT_MGR_LOG("[%s]out\n", __func__);
         return0;

}

回复

使用道具 举报

7

主题

15

帖子

171

积分

注册会员

Rank: 2

积分
171
 楼主| 发表于 2020-8-15 09:52:30
二、MTK HDMI通用驱动详情见kernel-3.18\drivers\misc\mediatek\ext_disp.c,这里只分析部分HDMI的工作流程;
1、  hdmi_init()函数会注册switch设备,该设备用于上报hdmi hotplug事件,hdmires_switch_data用来外部显示数据流的创建;
int hdmi_init(void)
{
         intret = 0;

         HDMI_ERR("start\n");
         /*for support hdmi hotplug, inform AP the event */
         hdmi_switch_data.name= "hdmi";
         hdmi_switch_data.index= 0;
         hdmi_switch_data.state= HDMI_STATE_NO_DEVICE;
         ret= switch_dev_register(&hdmi_switch_data);

         if(ret)
                   HDMI_ERR("[hdmi][HDMI]switch_dev_registerfailed, returned:%d!\n", ret);

         hdmires_switch_data.name= "res_hdmi";
         hdmires_switch_data.index= 0;
         hdmires_switch_data.state= 0;
         ret= switch_dev_register(&hdmires_switch_data);//接收用来创建外部显示数据路径

         if(ret)
                   HDMI_ERR("[hdmi][HDMI]switch_dev_registerfailed, returned:%d!\n", ret);
         HDMI_ERR("done\n");
         return0;
}

2、  hdmi_post_init()函数主要是获得HDMI具体IC的驱动接口,HDMI具体IC的初始化。

int hdmi_post_init(void)
{
         intboot_mode = 0;
         conststruct EXTD_DRIVER *extd_factory_driver = NULL;

         staticconst struct HDMI_UTIL_FUNCS hdmi_utils = {
                   .udelay= hdmi_udelay,
                   .mdelay= hdmi_mdelay,
                   .state_callback= hdmi_state_callback,
         };
         hdmi_drv= (struct HDMI_DRIVER *) HDMI_GetDriver();

         if(NULL == hdmi_drv) {
                   HDMI_ERR("[hdmi]%s,hdmi_init fail, can not get hdmi driver handle\n", __func__);
                   return-1;
         }

         hdmi_drv->set_util_funcs(&hdmi_utils);

         hdmi_params->init_config.vformat= HDMI_VIDEO_1280x720p_60Hz;
         hdmi_drv->get_params(hdmi_params);

         hdmi_drv->init();      /* need to remove to power on functionDonglei */
         if(hdmi_drv->register_callback != NULL) {
                   boot_mode= (int)get_boot_mode();
                   if(boot_mode == FACTORY_BOOT || boot_mode == ATE_FACTORY_BOOT) {
                            extd_factory_driver= EXTD_Factory_HDMI_Driver();
                            if(extd_factory_driver)
                                     extd_factory_driver->init();
                   }else

                   hdmi_drv->register_callback(hdmi_state_callback);
         }

         memset((void*)&hdmi_context, 0, sizeof(struct _t_hdmi_context));
         memset((void*)&extd_dpi_params, 0, sizeof(extd_dpi_params));

         p->output_mode= hdmi_params->output_mode;

         SET_HDMI_OFF();

         init_waitqueue_head(&hdmi_fence_release_wq);
         init_waitqueue_head(&hdmi_vsync_wq);

         Extd_DBG_Init();
         return0;
}

回复

使用道具 举报

7

主题

15

帖子

171

积分

注册会员

Rank: 2

积分
171
 楼主| 发表于 2020-8-15 09:54:00
三、HDMI IC的驱动分析,这里用的是lt8618sxb的芯片,具体分析见kernel-3.18\drivers\misc\mediatek\hdmi\lt8618sxb.c。
1lt8618sxb_init()主要是创建了hdmi的线程和定时器;
static int lt8618sxb_init(void)
{
         HDMI_DEF_LOG("lt8618sxb_init\n");
         init_waitqueue_head(&hdmi_timer_wq);
         hdmi_timer_task= kthread_create(hdmi_timer_kthread, NULL, "hdmi_timer_kthread");
         wake_up_process(hdmi_timer_task);
         init_waitqueue_head(&lt8618sxb_nlh_wq);
         lt8618sxb_nlh_task= kthread_create(lt8618sxb_nlh_kthread, NULL,"lt8618sxb_nlh_kthread");
         wake_up_process(lt8618sxb_nlh_task);
         memset((void*)&r_hdmi_timer, 0, sizeof(r_hdmi_timer));
         r_hdmi_timer.expires= jiffies + 1000 / (1000 / HZ);        /*wait 1s to stable */
         r_hdmi_timer.function= hdmi_poll_isr;
         r_hdmi_timer.data= 0;
         init_timer(&r_hdmi_timer);
         add_timer(&r_hdmi_timer);
         lt8618sxb_hdmi_debug_init();
         lt8618sxb_hdmi_factory_callback= NULL;
         return0;
}
2、  hdmi_timer_impl()就是在创建的HDMI的线程中调用的,主要是检测hdmi插拔状态(HDMI插拔状态是通过I2C读取IC寄存器的值,在此之前一定要确保I2C是正常通信的),并将插拔的状态上报到switch设备。
void hdmi_timer_impl(void)
{
         //HDMI_DEF_LOG("enter%s\n",__func__);
         //LT8618SX_Video_check();
//HDMI_DEF_LOG("lt8618sxb_hdmiinit:%d,lt8618sxb_hotinit:%d,lt8618sxb_hotplugstate:
%d\n",lt8618sxb_hdmiinit,lt8618sxb_hotinit,lt8618sxb_hotplugstate);
         if(lt8618sxb_hdmiinit == 0) {
                   lt8618sxb_hdmiinit= 1;
                   /*lt8618sxb_power_off(); */
                   //vInitAvInfoVar();
                   return;
         }
         if(lt8618sxb_hotinit != 1)
                   lt8618sxb_hdmiinit++;
#if defined(CONFIG_HAS_EARLYSUSPEND)
         if(lt8618sxb_hdmiearlysuspend == 1) {
#else
         {
#endif
                   if(((lt8618sxb_hdmiinit > 5) || (lt8618sxb_hotinit == 0)) &&(lt8618sxb_hotinit != 1)) {
                           
                            //MT8193_PLUG_LOG("enterhere!\n");
                            if(bCheckPordHotPlug(PORD_MODE | HOTPLUG_MODE) == FALSE) {
                                    
                                     if((lt8618sxb_hotplugstate == HDMI_STATE_HOT_PLUGIN_AND_POWER_ON)
                                         && (lt8618sxb_hotinit == 2)) {
                                               //vSetSharedInfo(SI_HDMI_RECEIVER_STATUS,HDMI_PLUG_OUT);
                                               lt8618sxb_hotplugstate= HDMI_STATE_HOT_PLUG_OUT;
                                               vPlugDetectService(HDMI_STATE_HOT_PLUG_OUT);
                                               MT8193_PLUG_LOG
                                                   ("[detectcable1] lt8618sxb_hotinit =%d,lt8618sxb_hdmiinit=%d\n",
                                                    lt8618sxb_hotinit, lt8618sxb_hdmiinit);
                                     }
#if 1
                                     if((lt8618sxb_hotinit == 0)
                                         && (bCheckPordHotPlug(HOTPLUG_MODE)== TRUE)) {
                                       
                                               
                                               lt8618sxb_hotinit= 2;
                                               lt8618sxb_hotplugstate= HDMI_STATE_HOT_PLUGIN_AND_POWER_ON;
                                               vPlugDetectService(HDMI_STATE_HOT_PLUGIN_AND_POWER_ON);
                                               //vWriteHdmiIntMask(0xff);   /* INT mask MDI */
                                               MT8193_PLUG_LOG
                                                   ("[detectcable2] lt8618sxb_hotinit =%d,lt8618sxb_hdmiinit=%d\n",
                                                    lt8618sxb_hotinit, lt8618sxb_hdmiinit);
                                     }
#endif
                                     if((lt8618sxb_hotinit == 0)
                                         && (bCheckPordHotPlug(HOTPLUG_MODE)== FALSE)) {
                                       
                                               
                                               lt8618sxb_hotinit= 2;
                                               
                                               lt8618sxb_hotplugstate= HDMI_STATE_HOT_PLUG_OUT;
                                               vPlugDetectService(HDMI_STATE_HOT_PLUG_OUT);
                                               MT8193_PLUG_LOG
                                                   ("[detectcable1] lt8618sxb_hotinit =%d,lt8618sxb_hdmiinit=%d\n",
                                                    lt8618sxb_hotinit, lt8618sxb_hdmiinit);
                                     }
                            }else if ((lt8618sxb_hotplugstate == HDMI_STATE_HOT_PLUG_OUT)
                                        && (bCheckPordHotPlug(PORD_MODE |HOTPLUG_MODE) == TRUE)) {
                                       
                                    
                                     lt8618sxb_hotplugstate= HDMI_STATE_HOT_PLUGIN_AND_POWER_ON;
                                     lt8618sxb_hotinit= 2;
                                     vPlugDetectService(HDMI_STATE_HOT_PLUGIN_AND_POWER_ON);
                                     //vWriteHdmiIntMask(0xff);   /* INT mask MDI */
                                     MT8193_PLUG_LOG
                                         ("[detectcable3] lt8618sxb_hotinit =%d,lt8618sxb_hdmiinit=%d\n",
                                          lt8618sxb_hotinit, lt8618sxb_hdmiinit);
                            }else if ((lt8618sxb_hotplugstate == HDMI_STATE_HOT_PLUGIN_AND_POWER_ON)
                                        && ((e_hdcp_ctrl_state ==HDCP_WAIT_RI)
                                            || (e_hdcp_ctrl_state ==HDCP_CHECK_LINK_INTEGRITY))) {
                                      //MT8193_PLUG_LOG("enter here2!\n");
                                     if(bCheckHDCPStatus(HDCP_STA_RI_RDY)) {
                                               vSetHDCPState(HDCP_CHECK_LINK_INTEGRITY);
                                               vSendHdmiCmd(HDMI_HDCP_PROTOCAL_CMD);
                                    
                                     }
                            }
                            lt8618sxb_hdmiinit= 1;
                   }
         }
         if(lt8618sxb_hdmiCmd == HDMI_PLUG_DETECT_CMD) {
                   vClearHdmiCmd();
                   /*vcheckhdmiplugstate(); */
                   /*vPlugDetectService(e_hdmi_ctrl_state); */
         }else if (lt8618sxb_hdmiCmd == HDMI_HDCP_PROTOCAL_CMD) {
                   vClearHdmiCmd();
                  
         }
}
总结:
HDMI工作流程就是linux kernel bootup,加载mtk_extd_mgr_init,获得HDMI驱动接口,匹配成功调用mtk_extd_mgr_probe,初始化hdmi,上层应用检测属性ro.mtk_hdmi_support=1,支持HDMI功能,setting hdmi poweron,底层收到MTK_HDMI_AUDIO_VIDEO_ENABLE将会hdmi_enable()hdmi_enable()调用hdmi_drv_init()hdmi_power_on();HDMI进入待机状态,等待hdmi设备的插入。
回复

使用道具 举报

返回列表
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


登录或注册
快速回复 返回顶部 返回列表