有谁知道这四个字是那家屏幕 mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_l

2937人阅读
linux驱动(7)
android display(24)
本文是Android Display部分分析的一部分,描述屏Framebuffer设备注册过程。
QC MSM7xxx/MSM8xxx平台本身就提供了很多接口的屏的支持,每种屏对应一个驱动文件。由于QC MSM平台显示驱动架构做了绝大部分的工作,驱动一块新的屏仅需要做很少量的工作。下面的过程是屏Framebuffer注册过程的分析。
设备资源申请是在MACHINE_DESC中实现的。示例如下:
3463MACHINE_START(MSM8930_CDP, &QCT MSM8930 CDP&)
3464 .map_io = msm8930_map_io,
3465 .reserve = msm8930_reserve,
3466 .init_irq = msm8930_init_irq,
3467 .handle_irq = gic_handle_irq,
3468 .timer = &msm_timer,
3469 .init_machine = msm8930_cdp_init,
3470 .init_early = msm8930_allocate_memory_regions,
3471 .init_very_early = msm8930_early_memory,
3472 .restart = msm_restart,
3473MACHINE_END
machine_desc{.init_very_early, .init_early, .init_machine, .restart}, module_init driver的初始化顺序参考
在machine_desc.init中做了许多machine级别设备的注册工作,主要意图是做设备资源分配。该.init函数部分示例代码如下:
static void __init msm8930_cdp_init(void) @ kernel/arch/arm/mach-msm/board-8930.c
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm8930_init_gpu();
msm8930_init_mmc();
msm8930_init_cam();
msm8930_init_fb();
其中,msm8930_cdp_init中与display相关的是msm8930_init_fb()函数,这个函数注册了几个id为0的设备。各主要设备名如下,
“msm_fb” msm framebuffer设备,注意不是linux framebuffer设备,但是有对应关系;
“wfd” wifi显示设备;
“mipi_dsi_cmd_samsung_fwvga”& mipi-dsi接口cmd模式LCD屏设备;
“hdmi_msm” HDMI显示器设备;
“mdp” mobile display station显示引擎设备;
“mipi-dsi” MIPI-DSI显示器驱动设备(id例外用了1,可能有的平台两个MIPI-DSI,另外一个id为0);
1168void __init msm8930_init_fb(void) @ kernel/arch/arm/mach-msm/board-8930-display.c
1170 platform_device_register(&msm_fb_device);
1172#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
1173 platform_device_register(&wfd_panel_device);
1174 platform_device_register(&wfd_device);
1175#endif
1178#ifdef CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
1179 platform_device_register(&mipi_dsi_novatek_panel_device);
1180#endif
1184#ifdef CONFIG_FB_MSM_MIPI_SA77_CMD_FWVGA_PANEL
1185 platform_device_register(&mipi_dsi_cmd_chimei_fwvga_panel_device);
1186 platform_device_register(&mipi_dsi_cmd_samsung_fwvga_panel_device);
1187#endif
1190#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
1191 platform_device_register(&hdmi_msm_device);
1192#endif
1194 platform_device_register(&mipi_dsi_toshiba_panel_device);
1196 msm_fb_register_device(&mdp&, &mdp_pdata);
1197 msm_fb_register_device(&mipi_dsi&, &mipi_dsi_pdata);
1198#ifdef CONFIG_MSM_BUS_SCALING
1199#ifdef CONFIG_FB_MSM_DTV
1200 msm_fb_register_device(&dtv&, &dtv_pdata);
1201#endif
1202#endif
因为注册这些设备的意图主要是资源申请和初步初始化设备,所以各设备注册顺序并无关紧要。其初始化顺序还与后来的驱动实际注册顺序有关。
首先注册paltform_device msm_fb_device,该设备定义如下:
71static struct resource msm_fb_resources[] = {
.flags = IORESOURCE_DMA,
135static struct msm_fb_platform_data msm_fb_pdata = {
136 .detect_client = msm_fb_detect_panel,
139static struct platform_device msm_fb_device = {
= &msm_fb&,
142 .num_resources
= ARRAY_SIZE(msm_fb_resources),
143 .resource
= msm_fb_resources,
144 .dev.platform_data = &msm_fb_pdata,
然后注册panel设备,定义如下:
845static struct mipi_dsi_panel_platform_data samsung_pdata = {
846 .enable_wled_bl_ctrl = 0x1,
849static struct platform_device mipi_dsi_cmd_samsung_fwvga_panel_device = {
850 .name = &dsi_cmd_samsung_fwvga&,
851 .id = 0,
852 .dev = {
.platform_data = &samsung_pdata,
然后关键的注册mdp和mipi-dsi controller.
1749void __init msm_fb_register_device(char *name, void *data)
1751 if (!strncmp(name, &mdp&, 3))
msm_register_device(&msm_mdp_device, data);
1753 else if (!strncmp(name, &lcdc&, 4))
msm_register_device(&msm_lcdc_device, data);
1755 else if (!strncmp(name, &mipi_dsi&, 8))
msm_register_device(&msm_mipi_dsi_device, data);
1757#ifdef CONFIG_FB_MSM_TVOUT
1758 else if (!strncmp(name, &tvenc&, 5))
msm_register_device(&msm_tvenc_device, data);
1760 else if (!strncmp(name, &tvout_device&, 12))
msm_register_device(&msm_tvout_device, data);
1762#endif
1763#ifdef CONFIG_MSM_BUS_SCALING
1764 else if (!strncmp(name, &dtv&, 3))
msm_register_device(&msm_dtv_device, data);
1766#endif
printk(KERN_ERR &%s: unknown device! %s\n&, __func__, name);
mdp和mipi-dsi设备及寄存器映射和中断需求如下
1484#define MIPI_DSI_HW_BASE 0x
1485#define ROTATOR_HW_BASE 0x04E00000
1486#define TVENC_HW_BASE
0x04F00000
1487#define MDP_HW_BASE
1489static struct resource msm_mipi_dsi_resources[] = {
= &mipi_dsi&,
= MIPI_DSI_HW_BASE,
= MIPI_DSI_HW_BASE + 0x000F0000 - 1,
= IORESOURCE_MEM,
= DSI_IRQ,
= DSI_IRQ,
= IORESOURCE_IRQ,
1503static struct platform_device msm_mipi_dsi_device = {
1504 .name
= &mipi_dsi&,
1506 .num_resources
= ARRAY_SIZE(msm_mipi_dsi_resources),
1507 .resource
= msm_mipi_dsi_resources,
1510static struct resource msm_mdp_resources[] = {
= MDP_HW_BASE,
= MDP_HW_BASE + 0x000F0000 - 1,
= IORESOURCE_MEM,
= INT_MDP,
= INT_MDP,
= IORESOURCE_IRQ,
1524static struct platform_device msm_mdp_device = {
1525 .name
1527 .num_resources
= ARRAY_SIZE(msm_mdp_resources),
1528 .resource
= msm_mdp_resources,
以上设备注册时,其驱动并未加载,因为machine_desc.init_machine设备注册的arch_initcall是在.initcall3.init中,而module_init
driver注册是在.initcall6.init中。等其驱动加载时,在对应的probe函数中,判断设备id,初步初始化设备,保存其资源分配。
此时,以下设备中id为0,mipi-dsi id为1的设备已经注册进系统了。
dsi_cmd_chimei_fwvga.0
dsi_cmd_samsung_fwvga.0
dsi_cmd_samsung_fwvga.1281
dtv.458753
hdmi_msm.0
hdmi_msm.1
mdp.458753
mdp.591105
mdp.655361
mipi_dsi.1
mipi_dsi.591105
mipi_toshiba.0
msm_fb.458753
msm_fb.591105
msm_fb.655361
下面描述各驱动注册,各驱动都是module_init的。
msm_fb驱动注册如下
module_init(msm_fb_init);
3898int __init msm_fb_init(void) @ kernel/drivers/video/msm/msm_fb.c
3900 int rc = -ENODEV;
3902 if (msm_fb_register_driver())
3705static int msm_fb_register_driver(void)
3707 return platform_driver_register(&msm_fb_driver);
msm_fb_driver驱动定义如下
734static struct platform_driver msm_fb_driver = {
735 .probe = msm_fb_probe,
736 .remove = msm_fb_remove,
737#ifndef CONFIG_HAS_EARLYSUSPEND
738 .suspend = msm_fb_suspend,
739 .resume = msm_fb_resume,
741 .shutdown = NULL,
742 .driver = {
/* Driver name must match the device name added in platform.c. */
.name = &msm_fb&,
.pm = &msm_fb_dev_pm_ops,
platform_device “msm_fb”的resource[0]是一块DMA内存,是framebuffer内存,但是在资源定义中并没有设置size,而在msm_fb_probe中从其使用可以看到该DMA内存已经分配。其size是在machine的init_early中从bootmem中分配的,比machine级别设备注册要早!
&&&&&&&&&&&&&&& .init_early = msm8930_allocate_memory_regions,
msm8930_allocate_memory_regions(void) @ kernel/arch/arm/mach-msm/board-8930.c
1006static void __init msm8930_allocate_memory_regions(void)
1008 msm8930_allocate_fb_region();
msm8930_allocate_fb_region() @ kernel/arch/arm/mach-msm/board-8930-display.c
1205void __init msm8930_allocate_fb_region(void)
1207 void *
1210 size = MSM_FB_SIZE;
1211 addr = alloc_bootmem_align(size, 0x1000);
1212 msm_fb_resources[0].start = __pa(addr);
1213 msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1;
1214 pr_info(&allocating %lu bytes at %p (%lx physical) for fb\n&, size, addr, __pa(addr));
MSM_FB_SIZE宏定义如下,TRIPLE_BUFFER已是主流;对应地SurfaceFlinger中FB layer也会分配有3个buffer。
32#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
33#define MSM_FB_PRIM_BUF_SIZE \
(roundup((1920 * 1088 * 4), 4096) * 3) /* 4 bpp x 3 pages */
36#define MSM_FB_PRIM_BUF_SIZE \
(roundup((1920 * 1088 * 4), 4096) * 2) /* 4 bpp x 2 pages */
39/* Note: must be multiple of 4096 */
40#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096)
当”msm_fb”注册时,msm_fb_probe在设备和驱动match后被调用。对于msm_fb_device id=0,只做fbram保存和ION
client创建;这时probe到的设备对应/sys/bus/platform/devices /msm_fb.0。msm_ion_client_create(-1, pdev-&name);”mipi-dsi”
driver定义和注册如下 (in kernel/drivers/video/msm/mipi_dsi.c)
55static struct platform_driver mipi_dsi_driver = {
56 .probe = mipi_dsi_probe,
57 .remove = mipi_dsi_remove,
58 .shutdown = NULL,
59 .driver = {
.name = &mipi_dsi&,
603static int mipi_dsi_register_driver(void)
605 return platform_driver_register(&mipi_dsi_driver);
608static int __init mipi_dsi_driver_init(void)
612 mipi_dsi_init();
614 ret = mipi_dsi_register_driver();
616 device_initialize(&dsi_dev);
618 if (ret) {
pr_err(&mipi_dsi_register_driver() failed!\n&);
626module_init(mipi_dsi_driver_init);
“mdp” driver定义和注册如下(in kernel/drivers/video/msm/mdp.c)
2094static struct platform_driver mdp_driver = {
2095 .probe = mdp_probe,
2096 .remove = mdp_remove,
2097#ifndef CONFIG_HAS_EARLYSUSPEND
2098 .suspend = mdp_suspend,
2099 .resume = NULL,
2100#endif
2101 .shutdown = NULL,
2102 .driver = {
* Driver name must match the device name added in
* platform.c.
.name = &mdp&,
.pm = &mdp_dev_pm_ops,
3001static int mdp_register_driver(void)
3003#ifdef CONFIG_HAS_EARLYSUSPEND
3004 early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
3005 early_suspend.suspend = mdp_early_
3006 early_suspend.resume = mdp_early_
3007 register_early_suspend(&early_suspend);
3008#endif
3010 return platform_driver_register(&mdp_driver);
3013static int __init mdp_driver_init(void)
3017 mdp_drv_init();
3019 ret = mdp_register_driver();
3020 if (ret) {
printk(KERN_ERR &mdp_register_driver() failed!\n&);
3025#if defined(CONFIG_DEBUG_FS)
3026 mdp_debugfs_init();
3027#endif
3029 return 0;
3033module_init(mdp_driver_init);
当真正从屏驱动中添加一块显示设备时,为了让上级设备(“mipi-dsi” “mdp” “msm_fb” “fb”)使用下级设备,高通实现为下级设备创建了每个上级设备的实例,通过从下到上的设备probe链一级一级向上注册。这时保证从下到上的设备注册顺序就是至关重要的了,probe链做注册来保证这一点。
当然为了达到上级设备使用和管理下级设备的目标,另一种方法是下级设备向上级设备做register,这时要保证下级设备向上级设备注册时,上级设备的用于管理的相关数据结构已经准备好。
下面描述由屏驱动添加屏到注册linux framebuffer设备的流程。
在各自的屏设备注册文件中,会去探测屏,这种探测不是做真正扫描,仅仅是使用设备名字验证一下,以SAMSUNG MIPI DSI CMD屏为例,驱动会使用相应规则ID注册一块屏。
static int __init mipi_cmd_samsung_fwvga_pt_init(void) @ kernel/drivers/video/msm/mipi_samsung_cmd_fwvga_pt.c
39 if (msm_fb_detect_client(&mipi_cmd_samsung_fwvga&))
88 ret = mipi_samsung_device_register(&pinfo, MIPI_DSI_PRIM, MIPI_DSI_PANEL_QHD_PT);
90 if (ret)
pr_err(&%s: failed to register device!\n&, __func__);
96module_init(mipi_cmd_samsung_fwvga_pt_init);
探测函数int msm_fb_detect_client(const char *name)首先使用主屏和外屏名字匹配,匹配不成则使用msm_fd_device.dev.platform .detect_client = msm_fb_detect_panel去匹配。上面欲同时注册SAMSUNG和CHIMEI两块屏设备,当然不能同时detect_panel成功,使用GPIO管脚配置做了二次匹配。实际不同批次的手机可能用到两块屏中的一种。
然后mipi_samsung_device_register()(对CHIMEI则是mipi_chimei_device_register)注册屏驱动和屏设备。驱动定义和注册具体如下,
358static struct platform_driver this_driver = {
359 .probe
= mipi_samsung_lcd_probe,
360 .driver = {
= &dsi_cmd_samsung_fwvga&,
365static struct msm_fb_panel_data samsung_panel_data = {
= mipi_samsung_lcd_on,
= mipi_samsung_lcd_off,
368 .set_backlight = mipi_samsung_set_backlight,
373int mipi_samsung_device_register(struct msm_panel_info *pinfo, u32 channel, u32 panel)
376 struct platform_device *pdev = NULL;
379 if ((channel &= 3) || ch_used[channel])
return -ENODEV;
382 ch_used[channel] = TRUE;
384 ret = mipi_samsung_lcd_init();
385 if (ret) {
pr_err(&mipi_samsung_lcd_init() failed with ret %u\n&, ret);
390 pdev = platform_device_alloc(&dsi_cmd_samsung_fwvga&, (panel && 8)|channel);
391 if (!pdev)
return -ENOMEM;
394 samsung_panel_data.panel_info = *
396 ret = platform_device_add_data(pdev, &samsung_panel_data,
sizeof(samsung_panel_data));
398 if (ret) {
printk(KERN_ERR
&%s: platform_device_add_data failed!\n&, __func__);
goto err_device_
404 ret = platform_device_add(pdev);
405 if (ret) {
printk(KERN_ERR
&%s: platform_device_register failed!\n&, __func__);
goto err_device_
411 return 0;
413err_device_put:
414 platform_device_put(pdev);
418static int mipi_samsung_lcd_init(void)
421 led_trigger_register_simple(&bkl_trigger&, &bkl_led_trigger);
422 pr_info(&%s: SUCCESS (WLED TRIGGER)\n&, __func__);
423 wled_trigger_initialized = 1;
425 mipi_dsi_buf_alloc(&samsung_tx_buf, DSI_BUF_SIZE);
426 mipi_dsi_buf_alloc(&samsung_rx_buf, DSI_BUF_SIZE);
428 return platform_driver_register(&this_driver);
在mipi_samsung_lcd_init()中注册platform_driver屏驱动,然后分配注册platform_device屏设备;platform_data设置为msm_fb_panel_data向上传递参数并用于上级设备调用控制屏开关和背光。
屏设备注册后,platform_device和platform_driver match,驱动的probe函数被调用,把参数一级一级向上带,用于设置上级设备参数,向上一级一级注册设备(“mipi-dsi”,
“mdp”, “msm_fb”, “framebuffer”)。
一个类似的调用栈如下
------------[ cut here ]------------
WARNING: at /home/CORPUSERS/xp010548/myworkdroid/7x25a/LINUX/kernel/drivers/video/msm/msm_fb.c:1221 msm_fb_probe+0xf4/0xcbc()
msm_fb_probe
Modules linked in:
[&c003fe0c&] (unwind_backtrace+0x0/0x12c) from [&c00adccc&] (warn_slowpath_common+0x4c/0x64)
[&c00adccc&] (warn_slowpath_common+0x4c/0x64) from [&c00add64&] (warn_slowpath_fmt+0x2c/0x3c)
[&c00add64&] (warn_slowpath_fmt+0x2c/0x3c) from [&c0223c44&] (msm_fb_probe+0xf4/0xcbc)
[&c0223c44&] (msm_fb_probe+0xf4/0xcbc) from [&c026e624&] (platform_drv_probe+0x18/0x1c)
[&c026e624&] (platform_drv_probe+0x18/0x1c) from [&c026d354&] (driver_probe_device+0x144/0x264)
[&c026d354&] (driver_probe_device+0x144/0x264) from [&c026c78c&] (bus_for_each_drv+0x48/0x84)
[&c026c78c&] (bus_for_each_drv+0x48/0x84) from [&c026d5d0&] (device_attach+0x74/0xa0)
[&c026d5d0&] (device_attach+0x74/0xa0) from [&c026c5a4&] (bus_probe_device+0x24/0x40)
[&c026c5a4&] (bus_probe_device+0x24/0x40) from [&c026b4c4&] (device_add+0x3f0/0x570)
[&c026b4c4&] (device_add+0x3f0/0x570) from [&c026ec54&] (platform_device_add+0x13c/0x1a0)
[&c026ec54&] (platform_device_add+0x13c/0x1a0) from [&c02264b4&] (mdp_probe+0x828/0x940)
[&c02264b4&] (mdp_probe+0x828/0x940) from [&c026e624&] (platform_drv_probe+0x18/0x1c)
[&c026e624&] (platform_drv_probe+0x18/0x1c) from [&c026d354&] (driver_probe_device+0x144/0x264)
[&c026d354&] (driver_probe_device+0x144/0x264) from [&c026c78c&] (bus_for_each_drv+0x48/0x84)
[&c026c78c&] (bus_for_each_drv+0x48/0x84) from [&c026d5d0&] (device_attach+0x74/0xa0)
[&c026d5d0&] (device_attach+0x74/0xa0) from [&c026c5a4&] (bus_probe_device+0x24/0x40)
[&c026c5a4&] (bus_probe_device+0x24/0x40) from [&c026b4c4&] (device_add+0x3f0/0x570)
[&c026b4c4&] (device_add+0x3f0/0x570) from [&c026ec54&] (platform_device_add+0x13c/0x1a0)
[&c026ec54&] (platform_device_add+0x13c/0x1a0) from [&c023db98&] (mipi_dsi_probe+0x514/0x5d0)
[&c023db98&] (mipi_dsi_probe+0x514/0x5d0) from [&c026e624&] (platform_drv_probe+0x18/0x1c)
[&c026e624&] (platform_drv_probe+0x18/0x1c) from [&c026d354&] (driver_probe_device+0x144/0x264)
[&c026d354&] (driver_probe_device+0x144/0x264) from [&c026c78c&] (bus_for_each_drv+0x48/0x84)
[&c026c78c&] (bus_for_each_drv+0x48/0x84) from [&c026d5d0&] (device_attach+0x74/0xa0)
[&c026d5d0&] (device_attach+0x74/0xa0) from [&c026c5a4&] (bus_probe_device+0x24/0x40)
[&c026c5a4&] (bus_probe_device+0x24/0x40) from [&c026b4c4&] (device_add+0x3f0/0x570)
[&c026b4c4&] (device_add+0x3f0/0x570) from [&c026ec54&] (platform_device_add+0x13c/0x1a0)
[&c026ec54&] (platform_device_add+0x13c/0x1a0) from [&c02223b8&] (msm_fb_add_device+0x150/0x1b4)
[&c02223b8&] (msm_fb_add_device+0x150/0x1b4) from [&c051e830&] (mipi_himax_lcd_probe+0x38/0x108)
[&c051e830&] (mipi_himax_lcd_probe+0x38/0x108) from [&c026e624&] (platform_drv_probe+0x18/0x1c)
[&c026e624&] (platform_drv_probe+0x18/0x1c) from [&c026d354&] (driver_probe_device+0x144/0x264)
[&c026d354&] (driver_probe_device+0x144/0x264) from [&c026c78c&] (bus_for_each_drv+0x48/0x84)
[&c026c78c&] (bus_for_each_drv+0x48/0x84) from [&c026d5d0&] (device_attach+0x74/0xa0)
[&c026d5d0&] (device_attach+0x74/0xa0) from [&c026c5a4&] (bus_probe_device+0x24/0x40)
[&c026c5a4&] (bus_probe_device+0x24/0x40) from [&c026b4c4&] (device_add+0x3f0/0x570)
[&c026b4c4&] (device_add+0x3f0/0x570) from [&c026ec54&] (platform_device_add+0x13c/0x1a0)
[&c026ec54&] (platform_device_add+0x13c/0x1a0) from [&c02409ec&] (mipi_himax_device_register+0x7c/0xc0)
[&c02409ec&] (mipi_himax_device_register+0x7c/0xc0) from [&c001ac34&] (mipi_cmd_himax_hvga_pt_init+0x148/0x180)
[&c001ac34&] (mipi_cmd_himax_hvga_pt_init+0x148/0x180) from [&c0034388&] (do_one_initcall+0x94/0x164)
[&c0034388&] (do_one_initcall+0x94/0x164) from [&c00083d8&] (kernel_init+0x98/0x144)
[&c00083d8&] (kernel_init+0x98/0x144) from [&c003b0d0&] (kernel_thread_exit+0x0/0x8)
---[ end trace 65f8ea ]---
下面考察设备probe链。
311static int __devinit mipi_samsung_lcd_probe(struct platform_device *pdev)
338 current_pdev = msm_fb_add_device(pdev);
340 if (current_pdev) {
mfd = platform_get_drvdata(current_pdev);
return -ENODEV;
if (mfd-&key != MFD_KEY)
return -EINVAL;
= &mfd-&panel_info.
if (phy_settings != NULL)
mipi-&dsi_phy_db = phy_
if (dlane_swap)
mipi-&dlane_swap = dlane_
355 return 0;
msm_fb_add_device做如下事情:
调用struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, u32 type, u32 id)函数分配platform_device “mipi-dsi.type_devid”并设置其platform_data为msm_fb_panel_data。
以额外msm_fb_data_type结构分配framebuffer。
注册”mipi-dsi”设备platform_device_add(this_dev)。
“mipi-dsi”设备和驱动进行attach, match后,“mipi-dsi” driver的probe被调用到。
static int mipi_dsi_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/mipi_dsi.c做如下事情(这次的设备ID是真正实例的了,不是1了):
分配”mdp”设备实例并设置其数据,设置”mipi-dsi”设备操作函数,用于向下操作一级一级设备;
继续设置linux framebuffer的参数;
根据屏分辨率设置mipi-dsi工作频率;
注册该”mdp”设备实例;
“mdp”设备和驱动进行attach, match后,“mdp” driver的probe被调用到。
static int mdp_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/mdp.c
分配”msm_fb”实例并设置其数据;
配置”mdp”工作模式;
注册”msm_fb”实例;
“msm_fb”设备和驱动进行attach, match后,“msm_fb” driver的probe被调用到。
static int msm_fb_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/msm_fb.c
调用msm_fb_register设置linux framebuffer并register_framebuffer。
至此,系统中相关设备如下:
dsi_cmd_chimei_fwvga.0
dsi_cmd_samsung_fwvga.0
dsi_cmd_samsung_fwvga.1281 -&mipi_dsi.591105 -& mdp.591105 -& msm_fb.591105 -& fbx
dtv.458753 -&mdp.458753 -& msm_fb.458753& -& fbx
hdmi_msm.0
hdmi_msm.1 -&mdp.655361 -& msm_fb.655361 -& fbx (hdmi)
mipi_dsi.1
mipi_toshiba.0
Framebuffer设备注册后,使用FBIOCPUT_VSCREEN或FBIOCPAN_DISPLAY,驱动都会使用msmfb_pan_display进行响应显示,由于涉及到MDP,另文分析。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:379762次
积分:4466
积分:4466
排名:第4575名
原创:61篇
转载:76篇
评论:54条
(1)(1)(4)(1)(3)(3)(12)(8)(13)(19)(1)(8)(6)(18)(9)(2)(7)(1)(1)(1)(5)(7)(6)简要记录了Qualcom MSM8xxx MDP Framebuffer驱动中的一些点。
Framebuffer设备的sysfs
330static int msm_fb_create_sysfs(struct platform_device *pdev)
332&&&&&&&&
333&&&&&&&& struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
335&&&&&&&& rc = sysfs_create_group(&mfd-&fbi-&dev-&kobj, &msm_fb_attr_group);
336&&&&&&&& if (rc)
337&&&&&&&&&&&&&&&&&&&&&&&& MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__,
338&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& rc);
339&&&&&&&&
root@android:/sys/class/graphics/fb0 # ls -al
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 bits_per_pixel
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 blank
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 console
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 cursor
-r--r--r-- root&&&& root&&&&&&&& -27 09:37 dev
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 mode
-rw-r--r-- root& &&&root&&&&&&&& -27 09:37 modes
-r--r--r-- root&&&& root&&&&&&&& -27 09:37 msm_fb_type
-r--r--r-- root&&&& root&&&&&&&& -27 09:37 name
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 pan
drwxr-xr-x root&&&& root& &&&&&&&&&&&& 08:28 power
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 rotate
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 state
-r--r--r-- root&&&& root&&&&&&&& -27 09:37 stride
lrwxrwxrwx root&&&& root&&&&&&&&&&& && 09:37 subsystem -& ../../../../class/graphics
-rw-r--r-- root&&&& root&&&&&&&& -27 08:28 uevent
-rw-r--r-- root&&&& root&&&&&&&& -27 09:37 virtual_size
-r--r--r-- root&&&& root&&&&&&&& -27 08:28 vsync_event
root@android:/sys/class/graphics/fb0 # cat msm_fb_type&&&&&&&&&&&&&&&&&&&&&&&&
mipi dsi cmd panel
root@android:/sys/class/graphics/fb0 # cat bits_per_pixel&&&&&&&&&&&&&&&&&&&&&
130|root@android:/sys/class/graphics/fb0 # cat dev
root@android:/sys/class/graphics/fb0 # cat modes
U:480x854p-0
root@android:/sys/class/graphics/fb0 # cat name
msmfb42_90501
root@android:/sys/class/graphics/fb0 # cat stride
root@android:/sys/class/graphics/fb0 # cat virtual_size&&&&&&&&&&&&&&&&&&&&&
cont_splash_done field
Add support for "Continuous Splash Screen" feature.
The image displayed on the screen by the android bootloaderdriver should continue till the android animation shows up.
Delay the display initialization for MDP, display dependent clocksand panel power on functions.
bootloader显示的image在linux内核启动过程中保持显示在屏幕上,知道开机动画显示,即linux内核启动过程中不要出现黑屏。
Early suspend & Early resume
Early suspend是有wakelock还占有,系统还不能整体suspend,但是可以关闭屏幕、背光、输入等;在Early suspended状态时,重新打开屏幕、背光和输入,是为对应的early resume。
fb_register中相关设置如下:
1551&&&&&&&&&&&&&&&&&&&&&& mfd-&early_suspend.suspend = msmfb_early_
1552&&&&&&&&&&&&&&&&&&&&&& mfd-&early_suspend.resume = msmfb_early_
1553&&&&&&&&&&&&&&&&&&&&&& mfd-&early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
1554&&&&&&&&&&&&&&&&&&&&&& register_early_suspend(&mfd-&early_suspend);
数据结构定义如下:
23/* The early_suspend structure defines suspend and resume hooks to be called
24 * when the user visible sleep state of the system changes, and a level to
25 * control the order. They can be used to turn off the screen and input
26 * devices that are not used for wakeup.
27 * Suspend handlers are called in low to high level order, resume handlers are
28 * called in the opposite order. If, when calling register_early_suspend,
29 * the suspend handlers have already been called without a matching call to the
30 * resume handlers, the suspend handler will be called directly from
31 * register_early_suspend. This direct call can violate the normal level order.
34&&&&&&&&&& EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
35&&&&&&&&&& EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
36&&&&&&&&&& EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
38struct early_suspend {
39#ifdef CONFIG_HAS_EARLYSUSPEND
40&&&&&&&&&& struct list_
41&&&&&&&&&&
42&&&&&&&&&& void (*suspend)(struct early_suspend *h);
43&&&&&&&&&& void (*resume)(struct early_suspend *h);
msm_fb_set_backlight以后是使用led_trigger调用真正led_classdev "wled"的brightnes_set去设置背光。
用户态ioctl通过msm_fb_set_backlight调用到msm_fb_panel_data::set_backlight,
"lcd_backlight".brightness_set -& msm_fb_panel_data::set_backlight -& "bkl_trigger".led_trigger -& "wled".brightness_set。然后找真正操作硬件IC部分。
驱动中设置背光则是绕过"lcd_backlight"设备直接通过backlight_worker工作执行到msm_fb_panel_data::set_backlight,然后-& "bkl_trigger".led_trigger -& "wled".brightness_set。
可以认为"lcd_backlight"是背光抽象设备,通过led_trigger的led组映射到不同的led_classdev设备
以三星DSI CMD屏为例:
In mipi_samsung.c
294static void mipi_samsung_set_backlight(struct msm_fb_data_type *mfd)
296&&&&&&&& if (!cci_fb_UpdateDone){
297&&&&&&&&&&&&&&&&&&&&&&&& printk("Taylor: No BL before LCM on\n");
298&&&&&&&&&&&&&&&&&&&&&&&&
299&&&&&&&& }
301&&&&&&&& pr_debug("Taylor: %s : Set BL:%d\n",__func__, mfd-&bl_level);
302&&&&&&&& if ((mipi_samsung_pdata-&enable_wled_bl_ctrl)
303&&&&&&&& &&& && (wled_trigger_initialized)) {
304&&&&&&&&&&&&&&&&&&&&&&&& led_trigger_event(bkl_led_trigger, mfd-&bl_level);
305&&&&&&&&&&&&&&&&&&&&&&&&
306&&&&&&&& }
kernel/drivers/leds/leds-pm8xxx.c
#define PM8XXX_LEDS_DEV_NAME&&&&&& "pm8xxx-led"
2283static struct platform_driver pm8xxx_led_driver = {
2284&&&&&& .probe&&&&&&&&&&&&&&&&& = pm8xxx_led_probe,
2285&&&&&& .remove&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& = __devexit_p(pm8xxx_led_remove),
2286&&&&&& .driver&&&&&&&&&&&&&&&&&& = {
2287&&&&&&&&&&&&&&&&&&&&&& .name&& = PM8XXX_LEDS_DEV_NAME,
2288&&&&&&&&&&&&&&&&&&&&&& .owner = THIS_MODULE,
2289&&&&&& },
pm8xxx_led_probe会对pm8038_led_info数组中的每个led使用设置led_classdev字段,并且初始化work item,然后使用led_classdev_register向系统注册每个led设备。
2197&&&&&&&&&&&&&&&&&&&&&& INIT_WORK(&led_dat-&work, pm8xxx_led_work);
2198&&&&&&&&&&&&&&&&&&&&&& INIT_WORK(&led_dat-&modework, pm8xxx_mode_work);
2199&&&&&&&&&&&&&&&&&&&&&& INIT_WORK(&led_dat-&testwork, pm8xxx_test_work);
每个led的brightness_set字段设置为pm8xxx_led_set。
1790static void pm8xxx_led_set(struct led_classdev *led_cdev,
1791&&&&&& enum led_brightness value)
1793&&&&&& struct&&& pm8xxx_led_data *
1795&&&&&& led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
1797&&&&&& if (value & LED_OFF || value & led-&cdev.max_brightness) {
1798&&&&&&&&&&&&&&&&&&&&&& dev_err(led-&cdev.dev, "Invalid brightness value exceeds");
1799&&&&&&&&&&&&&&&&&&&&&&
1800&&&&&& }
1802&&&&&& led-&cdev.brightness =
1803&&&&&& schedule_work(&led-&work);
1730static void pm8xxx_led_work(struct work_struct *work)
1732&&&&&&
1734&&&&&& struct pm8xxx_led_data *led = container_of(work,
1735&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& struct pm8xxx_led_data, work);
1737&&&&&& if (led-&pwm_dev == NULL) {
1738&&&&&&&&&&&&&&&&&&&&&& __pm8xxx_led_work(led, led-&cdev.brightness);
1739&&&&&& } else {
1740&&&&&&&&&&&&&&&&&&&&&& rc = pm8xxx_led_pwm_work(led);
1741&&&&&&&&&&&&&&&&&&&&&& if (rc)
1742&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pr_err("could not configure PWM mode for LED:%d\n",
1743&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& led-&id);
1744&&&&&& }
对PM8XXX_ID_WLED,是使用__pm8xxx_led_work
1692static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
1693&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& enum led_brightness level)
1695&&&&&&
1697&&&&&& mutex_lock(&led-&lock);
1699&&&&&& switch (led-&id) {
1700&&&&&& case PM8XXX_ID_LED_KB_LIGHT:
1701&&&&&&&&&&&&&&&&&&&&&& led_kp_set(led, level);
1702&&&&&&&&&&&&&&&&&&&&&&
1703&&&&&& case PM8XXX_ID_LED_0:
1704&&&&&& case PM8XXX_ID_LED_1:
1705&&&&&& case PM8XXX_ID_LED_2:
1706&&&&&&&&&&&&&&&&&&&&&& led_lc_set(led, level);
1707&&&&&&&&&&&&&&&&&&&&&&
1708&&&&&& case PM8XXX_ID_FLASH_LED_0:
1709&&&&&& case PM8XXX_ID_FLASH_LED_1:
1710&&&&&&&&&&&&&&&&&&&&&& led_flash_set(led, level);
1711&&&&&&&&&&&&&&&&&&&&&&
1712&&&&&&case PM8XXX_ID_WLED:
1713&&&&&&&&&&&&&&&&&&&&&&rc = led_wled_set(led, level);
1714&&&&&&&&&&&&&&&&&&&&&& if (rc & 0)
1715&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pr_err("wled brightness set failed %d\n", rc);
1716&&&&&&&&&&&&&&&&&&&&&&
1717&&&&&& case PM8XXX_ID_RGB_LED_RED:
1718&&&&&& case PM8XXX_ID_RGB_LED_GREEN:
1719&&&&&& case PM8XXX_ID_RGB_LED_BLUE:
1720&&&&&&&&&&&&&&&&&&&&&& led_rgb_set(led, level);
1721&&&&&&&&&&&&&&&&&&&&&&
1722&&&&&& default:
1723&&&&&&&&&&&&&&&&&&&&&& dev_err(led-&cdev.dev, "unknown led id %d", led-&id);
1724&&&&&&&&&&&&&&&&&&&&&&
1725&&&&&& }
1727&&&&&& mutex_unlock(&led-&lock);
led_wled_set写电源管理芯片pm8xxx的控制寄存器,控制wled。
Framebuffer fb_info::node
registered_fb它是一个数组,它的类型就是struct fb_info,它用于保存我们调用register_framebuffer传进来的struct fb_info。
num_registered_fb代表注册帧缓冲设备的个数。
& &1522 & & & & for (i = 0; i & FB_MAX; i++) & &1523 & & & & & & & & if (!registered_fb[i]) & &1524 & & & & & & & & & & & & & &1525 & & & & fb_info-&node =
相当于找到一个空的次设备号。
Framebuffer像素格式
主屏Framebuffer格式
RGBA8888, config时指定;屏格式为RGB565或RGB888,由overlay pipe做转换。
In mipi_dsi_probe()
481&&&&&&&& /*
482&&&&&&&& * get/set panel specific fb info
483&&&&&&&& */
484&&&&&&&& mfd-&panel_info = pdata-&panel_
485&&&&&&&& pinfo = &mfd-&panel_
487&&&&&&&& if (mfd-&panel_info.type == MIPI_VIDEO_PANEL)
488&&&&&&&&&&&&&&&&&&&&&&&& mfd-&dest = DISPLAY_LCDC;
489&&&&&&&& else
490&&&&&&&&&&&&&&&&&&&&&&&&
mfd-&dest = DISPLAY_LCD;
492&&&&&&&& if (mdp_rev == MDP_REV_303 &&
493&&&&&&&&&&&&&&&&&&&&&&&& mipi_dsi_pdata-&get_lane_config) {
494&&&&&&&&&&&&&&&&&&&&&&&& if (mipi_dsi_pdata-&get_lane_config() != 2) {
495&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pr_info("Changing to DSI Single Mode Configuration\n");
496#ifdef CONFIG_FB_MSM_MDP303
497&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& update_lane_config(pinfo);
499&&&&&&&&&&&&&&&&&&&&&&&& }
500&&&&&&&& }
502&&&&&&&& if (mfd-&index == 0)
503&&&&&&&&&&&&&&&&&&&&&&&&
mfd-&fb_imgType = MSMFB_DEFAULT_TYPE;&& // configed as RGBA8888 for fb0
504&&&&&&&& else
505&&&&&&&&&&&&&&&&&&&&&&&& mfd-&fb_imgType = MDP_RGB_565;
msmfb_update_notify/ msmfb_no_update_notify
用于CABL功能时,统计直方图使用;
更新屏幕前complete(&msmfb_update_notify)从而在准确时间点启动直方图统计;2*HZ后,msmfb_no_update_notify_timer超时,在timer超时回调中complete(&mfd-&msmfb_no_update_notify)结束直方图统计。
sw_refresher
使用定时器去触发mdp_refresh_screen,添加work进行dma_fnc调用。
针对某些接口的VIDEO模式屏且控制器没有hw_refresh支持时,可以使用sw_refresher。
FB_ACTIVATE_VBL标志涵义
fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL,表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来。这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中。
Views(...) Comments()

我要回帖

更多关于 有谁知道这是什么 的文章

 

随机推荐