一.framebuffer device注册
1. framebuffer定义
在alps/kernel/mediatek/platform/mt6577/kernel/core/mt6577_devs.c中
- #if defined(CONFIG_MTK_FB)
- static u64 mtkfb_dmamask = ~(u32)0;
- static struct resource resource_fb[] = {
- {
- .start = 0, //初始化start=0x3fb00000
- .end = 0, //end=0x3fffffff
- .flags = IORESOURCE_MEM
- }
- };
- static struct platform_device mt6577_device_fb = {
- .name = "mtkfb", //名字为mtkfb,与驱动匹配时用到
- .id = 0,
- .num_resources = ARRAY_SIZE(resource_fb),
- .resource = resource_fb,
- .dev = {
- .dma_mask = &mtkfb_dmamask,
- .coherent_dma_mask = 0xffffffff,
- },
- };
- #endif
2. 设备注册
在
alps/kernel/mediatek/platform/mt6577/kernel/core/mt6577_devs.c中
- __init int mt6577_board_init(void)
- {
- #if defined(CONFIG_MTK_FB)
- if (((bl_fb.base == FB_START) && (bl_fb.size == FB_SIZE)) ||(use_bl_fb == 2)) {
- mtkfb_set_lcm_inited(1);
- }
- resource_fb[0].start = FB_START; //将start与end初始化
- resource_fb[0].end = FB_START + FB_SIZE - 1;
-
- retval = platform_device_register(&mt6577_device_fb); //注册平台设备
- #endif
- }
3. 关于FB_SIZE
本来是一个简简单的宏定义,这儿却是一个函数,而且是稍微有些复杂的函数.
在mt6577_board_init中有如下:
if (((bl_fb.base == FB_START) && (bl_fb.size == FB_SIZE))
用了宏FB_SIZE,其定义如下:
#define FB_SIZE (RESERVED_MEM_SIZE_FOR_FB)
#define RESERVED_MEM_SIZE_FOR_FB (DISP_GetVRamSizeBoot((char*)&temp_command_line))
在alps/kernel/mediatek/source/kernel/drivers/video/disp_drv.c中
- UINT32 DISP_GetVRamSizeBoot(char *cmdline)
- {
- static UINT32 vramSize = 0;
- if(vramSize)
- {
- return vramSize;
- }
- disp_get_lcm_name_boot(cmdline); //3.1 从字符串中解析出lcm的名字,并提取函数指针
- if(disp_drv)
- vramSize = DISP_GetVRamSize(); //3.2 获取fb_size,现在是5M
- vramSize = ALIGN_TO_POW_OF_2(vramSize, 0x100000);
- return vramSize;
- }
3.1 解析硬件名,并获取函数指针
DISP_GetVRamSizeBoot
--> disp_get_lcm_name_boot
在alps/kernel/mediatek/source/kernel/drivers/video/disp_drv.c中
从命令行中传入的参数是:
console=ttyMT0,921600n1 root=/dev/ram vmalloc=280M slub_max_order=0 lcm=1-nt35516_3d fps=6020
- char disp_lcm_name[256] = {0};
- BOOL disp_get_lcm_name_boot(char *cmdline) //从命令字符串中解析出lcm的名字,并提取函数指针
- {
- p = strstr(cmdline, "lcm="); //解析出lcm字符串后面的就是lcm设备
- if(p == NULL)
- return DISP_SelectDeviceBoot(NULL); //如果没有找到 ret false
- //找到之后将全局变量isLCMFound设为1
- isLCMFound = 1;
- //将lcm设备名nt35516保存在disp_lcm_name中,当然要去掉多余的"1-"
- strncpy((char*)disp_lcm_name, (const char*)p, (int)(q-p));
- if(DISP_SelectDeviceBoot(disp_lcm_name))
- ret = TRUE;
- }
3.1.1 从list中找到硬件,并调用具体的函数指针
DISP_GetVRamSizeBoot
--> disp_get_lcm_name_boot
-->
DISP_SelectDeviceBoot
在alps/kernel/mediatek/source/kernel/drivers/video/disp_drv.c中
初始化lcm_drv, lcm_params , disp_drv这三个全局变量
- BOOL DISP_SelectDeviceBoot(const char* lcm_name)
- {
- LCM_DRIVER *lcm = NULL;
- //全局变量lcm_count = sizeof(lcm_driver_list)/sizeof(LCM_DRIVER*);
- //这儿用到了lcm_driver_list,在projectConfig.mk中可以配置list的项
- for(i = 0;i < lcm_count;i++) //lcm_count=1
- {
- lcm_params = &s_lcm_params;
- lcm = lcm_driver_list[i];
- lcm->set_util_funcs(&lcm_utils); //这个没什么用处
- lcm->get_params(lcm_params); //调用nt35516的lcm_get_params,获取参数
- if(lcm_count == 1)
- {
- lcm_drv = lcm; //找到lcm的驱动结构体之后就保存在全局变量lcm_drv中
- isLCMFound = TRUE; //标志isLCMFound设为true
- break;
- }
- }
- switch(lcm_params->type)
- {
- //将驱动的函数指针的保存在disp_drv中
- case LCM_TYPE_DBI : disp_drv = DISP_GetDriverDBI(); break;
- case LCM_TYPE_DPI : disp_drv = DISP_GetDriverDPI(); break;
- case LCM_TYPE_DSI : disp_drv = DISP_GetDriverDSI(); break; //这儿用的是这个
- default : ASSERT(0);
- }
- return TRUE;
- }
3.2 获取FB_SIZE
DISP_GetVRamSizeBoot
--> DISP_GetVRamSize
- UINT32 DISP_GetVRamSize(void)
- {
- static UINT32 vramSize = 0;
- if (0 == vramSize)
- {
- disp_drv_init_context();
- //获取fb_size=w*h*bpp/8*2=960*544*4*2=4177920
- vramSize = DISP_GetFBRamSize();
- ///get DXI working buffer size=0
- vramSize += disp_drv->get_working_buffer_size();
- //layerSize=w*h*2+4096=960*540*2+4096, total=5218816
- vramSize += DAL_GetLayerSize();
- //对齐到1M, 0x500000=5M
- vramSize = ALIGN_TO_POW_OF_2(vramSize, 0x100000);
- }
- return vramSize;
- }
二. fb驱动
在alps/kernel/mediatek/source/kernel/drivers/video/mtkfb.c中
module_init(mtkfb_init);
--> platform_driver_register(&mtkfb_driver)
- #define MTKFB_DRIVER "mtkfb"
- static struct platform_driver mtkfb_driver =
- {
- .driver = {
- .name = MTKFB_DRIVER, //驱动的name="mtkfb"正好与设备匹配,调用probe
- .bus = &platform_bus_type,
- .probe = mtkfb_probe,
- .remove = mtkfb_remove,
- .suspend = mtkfb_suspend,
- .resume = mtkfb_resume,
- .shutdown = mtkfb_shutdown,
- },
- };
1. 驱动的probe函数
- static int mtkfb_probe(struct device *dev)
- {
- struct platform_device *pdev;
- struct mtkfb_device *fbdev = NULL;
- struct fb_info *fbi;
-
- a.从uboot传给kernel的命令行字符串中解析出fps
- //console=ttyMT0,921600n1 root=/dev/ram vmalloc=280M slub_max_order=0
- //uboot_ver=2010.06 uboot_build_ver=MAIN2.2.ubt.4158 lcm=1-nt35516_3d fps=6004
- lcd_fps = simple_strtol(p, NULL, 10); //lcd_fps=6004
-
- if(DISP_IsContextInited() == FALSE) //在FB_SIZE宏中己经初始化过了,进入else
- {
- mtkfb_find_lcm_driver();
- }
- else
- {
- LCD_CHECK_RET(LCD_Init()); //1.1 硬件初始化并申请中断和等侍队列
- }
-
- //初始化下面这6个全局变量
- MTK_FB_XRES = DISP_GetScreenWidth();
- MTK_FB_YRES = DISP_GetScreenHeight();
- fb_xres_update = MTK_FB_XRES;
- fb_yres_update = MTK_FB_YRES;
- MTK_FB_BPP = DISP_GetScreenBpp();
- MTK_FB_PAGES = DISP_GetPages();
- init_waitqueue_head(&screen_update_wq); //又一个等侍队列
-
- //创建线程并唤醒, kthread_create之后并不会马上运行,需要wake_up
- screen_update_task = kthread_create(screen_update_kthread, NULL, "screen_update_kthread");
- wake_up_process(screen_update_task);
-
- //注册两个回调函数
- DISP_INTERRUPT_CALLBACK_STRUCT cbStruct;
- cbStruct.pFunc = mtkfb_lcd_complete_interrupt;
- cbStruct.pParam = NULL;
- DISP_SetInterruptCallback(DISP_LCD_TRANSFER_COMPLETE_INT, &cbStruct);
- DISP_INTERRUPT_CALLBACK_STRUCT cbStruct;
- cbStruct.pFunc = mtkfb_dpi_vsync_interrupt;
- cbStruct.pParam = NULL;
- DISP_SetInterruptCallback(DISP_DPI_VSYNC_INT, &cbStruct);
- init_state = 0;
- pdev = to_platform_device(dev);
- //申请framebuffer
- fbi = framebuffer_alloc(sizeof(struct mtkfb_device), dev);
-
- mtkfb_fbi = fbi;
- fbdev = (struct mtkfb_device *)fbi->par;
- fbdev->fb_info = fbi;
- fbdev->dev = dev;
- dev_set_drvdata(dev, fbdev);
- init_state++;
- fbdev->fb_size_in_byte = MTK_FB_SIZEV;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- fbdev->fb_pa_base = res->start; //获取fb的物理地址,并映射
- fbdev->fb_va_base = ioremap_nocache(res->start, res->end - res->start + 1);
-
- #if defined(MTK_M4U_SUPPORT)
- fb_va_m4u = fbdev->fb_pa_base;
- fb_size_m4u = fbdev->fb_size_in_byte;
- //为overlay的链表分配内存
- overlay_buffer_head = (struct fb_overlay_buffer_list*)vmalloc(sizeof(struct fb_overlay_buffer_list));
- overlay_buffer_head->next = NULL;
- #endif
- init_state++; // 2
- DISP_Init((DWORD)fbdev->fb_va_base, (DWORD)fbdev->fb_pa_base, is_lcm_inited);
-
- init_state++; // 3
- r = mtkfb_fbinfo_init(fbi); //初始化 fb_info
-
- init_state++; // 4
- r = mtkfb_register_sysfs(fbdev);
- init_state++; // 5
- r = register_framebuffer(fbi); //注册
- fbdev->state = MTKFB_ACTIVE;
- return 0;
- }
1.1 硬件初始化并申请中断+初始化等侍队列
- LCD_STATUS LCD_Init(void)
- {
- memset(&_lcdContext, 0, sizeof(_lcdContext));
- _ResetBackupedLCDRegisterValues();
- ret = LCD_PowerOn();
- LCD_OUTREG32(&LCD_REG->SYNC_LCM_SIZE, 0x00010001);
- LCD_OUTREG32(&LCD_REG->SYNC_CNT, 0x1);
- //申请中断和等侍队列
- request_irq(MT6577_LCD_IRQ_ID, _LCD_InterruptHandler, IRQF_TRIGGER_LOW, "mtklcd", NULL);
- init_waitqueue_head(&_lcd_wait_queue);
- LCD_REG->INT_ENABLE.COMPLETED = 1;
- LCD_REG->INT_ENABLE.CMDQ_COMPLETED = 1;
- LCD_REG->INT_ENABLE.HTT = 1;
- LCD_REG->INT_ENABLE.SYNC = 1;
- return LCD_STATUS_OK;
- }
- static irqreturn_t _LCD_InterruptHandler(int irq, void *dev_id)
- {
- LCD_REG_INTERRUPT status = LCD_REG->INT_STATUS;
- if (status.COMPLETED)
- {
- wake_up_interruptible(&_lcd_wait_queue);
- //即调用mtkfb_lcd_complete_interrupt
- if(_lcdContext.pIntCallback)
- _lcdContext.pIntCallback(DISP_LCD_TRANSFER_COMPLETE_INT);
- }
- if (status.SYNC)
- {
- //DISP_LCD_SYNC_INT即调用mtkfb_lcd_complete_interrupt
- if(_lcdContext.pIntCallback)
- _lcdContext.pIntCallback(DISP_LCD_SYNC_INT);
- lcd_esd_check = false;
- }
- LCD_OUTREG32(&LCD_REG->INT_STATUS, 0);
- return IRQ_HANDLED;
- }
- static void mtkfb_lcd_complete_interrupt(void *param)
- {
- if(atomic_read(&has_pending_update))
- {
- wake_up_interruptible(&screen_update_wq);
- }
- #if defined(MTK_HDMI_SUPPORT)
- hdmi_source_buffer_switch();
- if(is_hdmi_active())
- {
- hdmi_update();
- }
- #endif
- }
线程函数中wait_event
- static int screen_update_kthread(void *data)
- {
- struct sched_param param = { .sched_priority = RTPM_PRIO_SCRN_UPDATE };
- sched_setscheduler(current, SCHED_RR, ¶m);
- for( ;; ) {
- wait_event_interruptible(screen_update_wq, atomic_read(&has_pending_update));
- MTKFB_LOG("wq wakeup\n");
- mtkfb_update_screen_impl();
- atomic_set(&has_pending_update,0);
- if (kthread_should_stop())
- break;
- }
- return 0;
- }