framebuffer分析之fb的注册
1.
board-mx6q_sabresd.c里定义了4个平台设备私有数据,最终分别代表fb0,fb1,fb2,fb3
static struct ipuv3_fb_platform_data sabresd_fb_data[] = {
{ /*fb0*/
.disp_dev = "ldb1",
.interface_pix_fmt = IPU_PIX_FMT_RGB666,
.mode_str = "LDB-XGA",
.default_bpp = 16,
.int_clk = false,
.late_init = false,
}, {
.disp_dev = "ldb2",
.interface_pix_fmt = IPU_PIX_FMT_RGB666,
.mode_str = "LDB-XGA",
.default_bpp = 16,
.int_clk = false,
}, {
.disp_dev = "lcd3",
.interface_pix_fmt = IPU_PIX_FMT_RGB565,
.mode_str = "CLAA-WVGA",
.default_bpp = 16,
.int_clk = false,
.late_init = false,
}, {
.disp_dev = "ldb4",
.interface_pix_fmt = IPU_PIX_FMT_RGB666,
.mode_str = "LDB-VGA",
.default_bpp = 16,
.int_clk = false,
.late_init = false,
},
};
但是uboot参数中的video域可以更新掉其中的某一个,也就是说video域也是构造fb的平台设备私有数据的一种方式。比如
video=mxcfb1:dev=adv739x,BT656-PAL,if=BT656,fbpix=RGB565
则fb1就是adv739x,替换掉了ldb2.
替换过程见mxcfb_option_setup函数。
2.
board-mx6q_sabresd.c的board_init函数中,执行4次imx6q_add_ipuv3fb,
for (i = 0; i < 4 && i < ARRAY_SIZE(sabresd_fb_data); i++)
imx6q_add_ipuv3fb(i, &sabresd_fb_data[i]);
imx6q_add_ipuv3fb函数最终调用platform-imx_ipuv3.c里的imx_add_platform_device_dmamask("mxc_sdc_fb", id,NULL, 0, pdata, sizeof(*pdata),DMA_BIT_MASK(32));
可见是注册了4次平台设备。每次注册平台设备都会触发平台驱动的probe函数,往下看。
3.
平台驱动位于mxc_ipuv3_fb.c,在mxcfb_probe函数中创建fbi结构体
static int mxcfb_probe(struct platform_device *pdev)
{
struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
struct resource *res;
int ret = 0;
/* Initialize FB structures */
fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);//重要
if (!fbi) {
ret = -ENOMEM;
goto init_fbinfo_failed;
}
ret = mxcfb_option_setup(pdev, fbi);
if (ret)
goto get_fb_option_failed;
mxcfbi = (struct mxcfb_info *)fbi->par;
mxcfbi->ipu_int_clk = plat_data->int_clk;
mxcfbi->late_init = plat_data->late_init;
mxcfbi->fb_pix_fmt = plat_data->fb_pix_fmt;
mxcfbi->first_set_par = true;
ret = mxcfb_dispdrv_init(pdev, fbi);//重要
if (ret < 0)
goto init_dispdrv_failed;
ret = ipu_test_set_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);//重要
if (ret < 0) {
dev_err(&pdev->dev, "ipu%d-di%d already in use\n",
mxcfbi->ipu_id, mxcfbi->ipu_di);
goto ipu_in_busy;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res && res->start && res->end) {
fbi->fix.smem_len = res->end - res->start + 1;
fbi->fix.smem_start = res->start;
fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
/* Do not clear the fb content drawn in bootloader. */
if (!mxcfbi->late_init)
memset(fbi->screen_base, 0, fbi->fix.smem_len);
}
mxcfbi->ipu = ipu_get_soc(mxcfbi->ipu_id);
if (IS_ERR(mxcfbi->ipu)) {
ret = -ENODEV;
goto get_ipu_failed;
}
/* first user uses DP with alpha feature */
if (!g_dp_in_use[mxcfbi->ipu_id]) {
mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK;
mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
mxcfbi->ipu_ch = MEM_BG_SYNC;
/* Unblank the primary fb only by default */
if (pdev->id == 0)
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
else
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
ret = mxcfb_register(fbi);//重要
if (ret < 0)
goto mxcfb_register_failed;
ipu_disp_set_global_alpha(mxcfbi->ipu, mxcfbi->ipu_ch,
true, 0x80);
ipu_disp_set_color_key(mxcfbi->ipu, mxcfbi->ipu_ch, false, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ret = mxcfb_setup_overlay(pdev, fbi, res);
if (ret < 0) {
mxcfb_unregister(fbi);
goto mxcfb_setupoverlay_failed;
}
g_dp_in_use[mxcfbi->ipu_id] = true;
ret = device_create_file(mxcfbi->ovfbi->dev,
&dev_attr_fsl_disp_property);
if (ret)
dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
"file for disp property\n",
ret);
ret = device_create_file(mxcfbi->ovfbi->dev,
&dev_attr_fsl_disp_dev_property);
if (ret)
dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
"file for disp device "
"propety\n", ret);
} else {
mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK;
mxcfbi->ipu_alp_ch_irq = -1;
mxcfbi->ipu_ch = MEM_DC_SYNC;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
ret = mxcfb_register(fbi);
if (ret < 0)
goto mxcfb_register_failed;
}
platform_set_drvdata(pdev, fbi);
ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);
if (ret)
dev_err(&pdev->dev, "Error %d on creating file for disp "
"property\n", ret);
ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
if (ret)
dev_err(&pdev->dev, "Error %d on creating file for disp "
" device propety\n", ret);
#ifdef CONFIG_LOGO
fb_prepare_logo(fbi, 0);//重要
fb_show_logo(fbi, 0);
#endif
return 0;
mxcfb_setupoverlay_failed:
mxcfb_register_failed:
get_ipu_failed:
ipu_clear_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);
ipu_in_busy:
init_dispdrv_failed:
fb_dealloc_cmap(&fbi->cmap);
framebuffer_release(fbi);
get_fb_option_failed:
init_fbinfo_failed:
return ret;
}
一一分析上面标记重要的几个函数
一、mxcfb_init_fbinfo,
内部调用 framebuffer_alloc();来分配fbi和fbi的私有指针即mxcfbi的内存
二、mxcfb_option_setup
用来解析uboot中的video参数比如“video=mxcfb1:dev=adv739x,BT656-PAL,if=BT656,fbpix=RGB565 video=mxcfb2:dev=hdmi,1280x720M@60,if=RGB24,fbpix=RGB565”
第一次调用mxcfb_option_setup会查找名字是mxcfb0的uboot参数,第二次查找名字是mxcfb1的uboot参数,第三次查找名字是mxcfb2的uboot参数,,这个函数随着probe函数共计调用了4次。所以本例中,最终fb0是ldb1,fb1是adv739x,fb2是hdmi,fb3是ldb4.
三、mxcfb_dispdrv_init
uboot的video域更新了fb1和fb2的平台私有数据。fb0和fb3的没有更新,还是使用board-mx6q_sabresd.c的sabresd_fb_data数组里面的。
而私有数据的结构体是ipuv3_fb_platform_data
struct ipuv3_fb_platform_data {
char disp_dev[32];
u32 fb_pix_fmt;
u32 interface_pix_fmt;
char *mode_str;
int default_bpp;
bool int_clk;
bool late_init;
};
其中,disp_dev是连接显示设备驱动和fb驱动的标示:在mxcfb_dispdrv_init函数中调用mxc_dispdrv_gethandle函数来根据disp_dev获取到对应的显示设备,并得到其驱动指针mxc_dispdrv_driver。同时调用mxc_dispdrv_driver的init函数,来初始化显示设备。
mxc_dispdrv_driver几乎是显示设备的全部家当(就像fbi是fb的全部家当一样),所有的东东都在这个结构体里面,下篇会讲。
四、mxcfb_register
此函数会调用register_framebuffer来注册fb,生成fbn。第一次调用register_framebuffer会生成fb0,第二次是fb1,第三次是fb2,,,顺序加1的。
register_framebuffer定义在fbmem.c中
register_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(®istration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);
return ret;
}