HDMI在目前的嵌入式中应用越来越广,关于其定义,相关的cec,edid,hdcp等不做过多介绍。
相对于其他几家芯片厂家的hdmi模块,
ti的hdmi子系统又有很多的不同。
dss可以理解为Display Subsystem的简写,翻译过来就是显示子系统,这个硬件模块负责读从存储器取像素数据,发送到lcd或者hdmi显示器。
它分为两大部分:1)DISPC,获取像素数据,颜色转换,组成和其他像素操作;2)编码器,将原始像素数据转化成标准的显示信息,hdmi,或者mipi DPI。
对于一个芯片来说dss还应该包括其他一些有用的解码器,比如dpi dvi等等,还有显示面板。
DISPC又包括overlays和overlay managers,简单的理解,前者负责接收数据,后者负责数据处理。
overlays又分为GFX和VIDEO ,各自支持一些格式,值得注意的是后者支持yuv,而前者不支持。
dss也有一些不支持的属性,不支持HDCP。dss2是dss的升级版本支持hdcp。
下图是dss整体框架。
上面只是dss模块的整体框架,在实际编码中有很多微小的细节需要注意。
sii9022是
开发板使用的hdmi芯片,是Silicon Image公司的产品,支持HDMI 1.2a(1.4应该是目前最新的版本)和DVI 1.0,以及下面一些特性。
- Integrated TMDS core
- • DTV resolution support - 480i/576i/
- 480p/576p/720p/1080i/1080p
- • PC resolution support - VGA/XGA/
- SXGA/WSXGA/UXGA
- • Flexible interface to HD MPEG decoders:
- - 12/24-bit RGB YCbCr 4:4:4
- - 16/20/24-bit YCbCr 4:2:2
- - 8/10/12-bit YCbCr 4:2:2
- (ITU-R BT.601 & BT.656)
- • Integrated YCbCr —> RGB conversion
- • 4:2:2 —> 4:4:4 up-converter
- • Programmable Data Enable (DE) generator
sii9022
威廉希尔官方网站
图如下:
下面就简单分析一下hdmi的启动流程。
-
- DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
- .reserve = am33xx_reserve,
- .map_io = am33xx_map_io,
- .init_early = am33xx_init_early,
- .init_late = am33xx_init_late,
- .init_irq = omap_intc_of_init,
- .handle_irq = omap3_intc_handle_irq,
- .init_machine = omap_generic_init,
- .init_late = am33xx_init_late,
- .init_time = omap3_gptimer_timer_init,
- .dt_compat = am33xx_boards_compat,
- .restart = am33xx_restart,
- MACHINE_END
根据启动log我们可以找到一个入口函数,他的实现是在arch/
ARM/mach-omap2中的board-genreic.c文件中。当然kernel的最开始入口函数不是这个函数,但是对于我们关注的某个soc来说,把
DT_MACHINE_START函数作为入口函数足够了。
omap_generic_init -->omapdss_init_of(display.c) -->omap_init_fb(fb.c另外两个函数为return 0;)初始化fb等等。
下面omap_dss_init函数通过module_init(omap_dss_init);加载到kernel中,其实现如下,调用探针函数 omap_dss_probe,初始化dss_init_platform_driver和dispc_init_platform_driver,前者初始化dss2,后者启动dispc,跟前面的介绍很吻合。
- static int __init omap_dss_init(void)
- {
- int r;
- int i;
- r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
- if (r)
- return r;
-
- r = dss_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize DSS platform drivern");
- goto err_dss;
- }
-
- r = dispc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize dispc platform drivern");
- goto err_dispc;
- }
-
- /*
- * It's ok if the output-driver register fails. It happens, for example,
- * when there is no output-device (e.g. SDI for OMAP4).
- */
- for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
- r = dss_output_drv_reg_funcs[i]();
- if (r == 0)
- dss_output_drv_loaded[i] = true;
- }
-
- dss_initialized = true;
- printk(" KERN_INFO =5=omap_dss_initn");
-
- return 0;
-
- err_dispc:
- dss_uninit_platform_driver();
- err_dss:
- platform_driver_unregister(&omap_dss_driver);
-
- return r;
- }
module_platform_driver(omapfb_driver);函数将omapfb_driver模块加载到系统中,且与上面的omap_init_fb函数内容呼应。注意
def_mode的数值是如何获取的。
- static struct platform_driver omapfb_driver = {
- .probe = omapfb_probe,
- .remove = __exit_p(omapfb_remove),
- .driver = {
- .name = "omapfb",
- .owner = THIS_MODULE,
- },
- };
- /*Kernel command line: console=ttyO0,115200n8
- omapdss.def_disp=display1
- omapfb.mode=display1:1024x768MR-24@60
- root=/dev/nfs nfsroot=192.168.9.217:/opt/nfs_dir,nolock rw ip=dhcp
- */
- module_param_named(mode, def_mode, charp, 0);
- module_param_named(vram, def_vram, charp, 0);
- module_param_named(rotate, def_rotate, int, 0);
- module_param_named(vrfb, def_vrfb, bool, 0);
- module_param_named(mirror, def_mirror, bool, 0);
- module_platform_driver(omapfb_driver);
module_platform_driver(hdmi_connector_driver);函数将hdmi_connector_driver模块加载到系统中,与下面的内容呼应
- hdmi0: connector@1 {
- compatible = "ti,hdmi_connector";
- video-source = <&sii9022>;
- };
module_i2c_driver(sil9022_driver); 函数将sil9022_driver模块加载到系统中,且与下面的内容呼应。注意其是module_i2c_driver函数,从这里才能找到一点其他soc中hdmi模块的影子。
- module_i2c_driver(sil9022_driver);
- sii9022: sii9022@3b {
- compatible = "sii,sii9022";
- reg = <0x3b>;
- reset-gpio = <&gpio5 8 GPIO_ACTIVE_LOW>;
- video-source = <&dpi>;
- data-lines = <24>;
- };
dts中的定义也与下面iic的设备节点对应起来。0x3b
module_platform_driver(panel_dpi_driver);函数将panel_dpi_driver加载到系统中,
- static struct platform_driver panel_dpi_driver = {
- .probe = panel_dpi_probe,
- .remove = __exit_p(panel_dpi_remove),
- .driver = {
- .name = "panel-dpi",
- .owner = THIS_MODULE,
- .of_match_table = panel_dpi_of_match,
- },
- };
panel_dpi_driver与下面的内容呼应。
- lcd0: display@0 {
- compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
- video-source = <&dpi>;
- data-lines = <24>;
- panel-timing {
- clock-frequency = <36000000>;
- hactive = <800>;
- vactive = <480>;
- hfront-porch = <210>;
- hback-porch = <1>;
- hsync-len = <43>;
- vback-porch = <1>;
- vfront-porch = <22>;
- vsync-len = <22>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <1>;
- pixelclk-active = <1>;
- };
- };
根据下面的log,我们可以清晰的看到fb,sii9022,panel-pdi,dss是如何串联起来。
- =6=omapfb_probe
- =1=omapfb_init_connections
- =1=hdmic_connect
- =1=hdmic_connect in->ops.hdmi->connect
- =1=sil9022_connect
- sii9022 1-003b: CONNECT
- =2=omapfb_init_connections fbdev->num_displays=2
- =2=omapfb_init_connections fbdev->num_displays=2 i=0
- =1=panel_dpi_connect
- =2=panel_dpi_connect
- omapdss APPLY error: manager lcd is already connected to an output
- fbcvt: 1024x768@60: CVT Name - .786M3-R
- =7=omapfb_probe
- create 3 framebuffers
- fb_infos allocated
- =1=omapdss_default_get_resolution
- fbmems allocated
- =1=omapdss_default_get_resolution
- fb_infos initialized
- =1=omapfb_set_par
- =2=omapfb_set_par
- =3=omapfb_set_par
- =1=omapfb_apply_changes
- =4=omapfb_set_par
- pan_display(0)
- Console: switching to colour frame buffer device 128x48
- pan_display(0)
- framebuffers registered
- =1=omapfb_apply_changes
- =1=omapfb_apply_changes
- =1=omapfb_apply_changes
- Enable fb0
- create_framebuffers done
- =8=omapfb_probe
- =1=omapfb_init_display
- =1=sil9022_enable
- sii9022 1-003b: ENABLE
- =2=sil9022_enable
- =3=sil9022_enable
- =1=sil9022_hw_enable
- sii9022 1-003b: HW_ENABLE -> Timings
- pixel_clk = 56000
- horizontal res = 1024
- vertical res = 768
- sii9022 1-003b: hdmi enabled
- =4=sil9022_enable
- =2=omapfb_init_display
- =3=omapfb_init_display
- =6=omapfb_init_display
- =9=omapfb_probe
omapdss的节点有哪些?
sysfs接口测试
cat /sys/devices/platform/omapdss/overlay0/name
gfx
与介绍中的概念对应起来。
echo "0" > /sys/devices/platform/omapdss/overlay0/enabled
echo "1" > /sys/devices/platform/omapdss/overlay0/enabled
上面两个语句可以控制屏幕的亮灭。
echo 2 > /sys/class/graphics/fb0/rotate可以控制屏幕的旋转180度,
只是我们这里运行出错。
另外还有一个问题,不知道大家注意到没有dts和相应的probe函数一一对应,他们会调用几次?
一般情况是一次,但是如果我们想多次调用探针函数可以吗?答案是肯定可以的。
掌握上面的信息,我感觉对于开发板hdmi模块已经有了一个简单的入门,当然更加细节的问题则需要时间继续研究,比如edid如何处理,HDCP如何实现。
帧缓冲(framebuffer)是 Linux 为显示设备提供的一个接口,出现在kernel2.2.x以后,它把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作,写操作可以立即反应到显示屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer 设备驱动来完成的,所以可知其具有良好的移植性。
帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux 下还可支持多个帧缓冲设备,最多可达32 个,分别为/dev/fb0 到/dev/fb31,当前缺省的帧缓冲设备通常指向/dev/fb0。当然在嵌入式系统中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。在MID上,设备信息是/dev/graphics/fb0。
对于我们来说omapfb_driver驱动实现了对framebuffer的支持。
- fh = openFB(NULL);
- getVarScreenInfo(fh, &var);
- getFixScreenInfo(fh, &fix);
-
- // fb_mem_offset = (unsigned long)(fix.smem_start) & (~PAGE_MASK);
- fb_mem = (unsigned long int)mmap(NULL, fix.smem_len ,
- PROT_READ | PROT_WRITE, MAP_SHARED, fh, 0);
- memcpy((void*)(fb_mem + offset), color, 4);
- closeFB(fh);
上面是帧缓冲的简单步骤,附件是我显示bmp的源代码。
下面是运行的结果,一图是没有运行的抓图,二图是显示第一个bmp,三图是显示第二个bmp。
知识点总结:
1.dss;
2.sii9022;
3.probe函数多次调用;
4.framebuffer。