mini2440 LCD驱动
来源:互联网 发布:c语言杨辉三角不用数组 编辑:程序博客网 时间:2024/06/10 01:56
mini2440集成了lcd控制器的接口,板子上接的lcd硬件是统宝240*320,TFT型lcd。lcd驱动对应的文件为s3c2410fb.c。要读懂这个驱动必须了解linux platform子系统的知识。因为这个驱动是以platform驱动的形式注册到内核。而且还需要frambuffer驱动的知识,因为这个驱动还是frambuffer接口的。lcd驱动在模块初始化的时候,调用platform注册函数将自己注册到内核,利用linux设备模型核心的机制调用platform_bus总线的match函数找到相应的设备,然后由linux设备模型核心调用s3c2410fb.c中的s3c2410fb_probe ,进行硬件相关初始化,并初始化frambuffer结构。然后注册到frambuffer核心。lcd的功能实现通过frambuffer核心来完成。s3c2410fb.c的功能实现都是配合frambuffer核心的。下面详细分析lcd驱动的实现。
程序基本结构
1.模块初始化-->向platform核心注册自己
2.实现linux设备模型必须的probe函数-->向frambuffer核心注册自己(最重要)
resume函数-->系统在由挂起恢复的时候调用
suspand-->系统在挂起的时候调用
remove--> 驱动程序注销自己的时候调用
3.frambuffer驱动模型fb_ops各函数的实现-->实现fb驱动的ioctl命令需要的函数
4.其他函数-->由2.3.中的函数调用,帮助其实现功能。
一. 相关数据结构
1. struct fb_info 结构
程序基本结构
1.模块初始化-->向platform核心注册自己
2.实现linux设备模型必须的probe函数-->向frambuffer核心注册自己(最重要)
resume函数-->系统在由挂起恢复的时候调用
suspand-->系统在挂起的时候调用
remove--> 驱动程序注销自己的时候调用
3.frambuffer驱动模型fb_ops各函数的实现-->实现fb驱动的ioctl命令需要的函数
4.其他函数-->由2.3.中的函数调用,帮助其实现功能。
一. 相关数据结构
1. struct fb_info 结构
1.struct fb_info {
2. int node;
3. int flags;
4. struct mutex lock; /* Lock for open/release/ioctl funcs */
5. struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
6. struct fb_var_screeninfo var; /* Current var */
7. struct fb_fix_screeninfo fix; /* Current fix */
8. struct fb_monspecs monspecs; /* Current Monitor specs */
9. struct work_struct queue; /* Framebuffer event queue */
10. struct fb_pixmap pixmap; /* Image hardware mapper */
11. struct fb_pixmap sprite; /* Cursor hardware mapper */
12. struct fb_cmap cmap; /* Current cmap */
13. struct list_head modelist; /* mode list */
14. struct fb_videomode *mode; /* current mode */
15.
16.#ifdef CONFIG_FB_BACKLIGHT
17. /* assigned backlight device */
18. /* set before framebuffer registration,
19. remove after unregister */
20. struct backlight_device *bl_dev;
21.
22. /* Backlight level curve */
23. struct mutex bl_curve_mutex;
24. u8 bl_curve[FB_BACKLIGHT_LEVELS];
25.#endif
26.#ifdef CONFIG_FB_DEFERRED_IO
27. struct delayed_work deferred_work;
28. struct fb_deferred_io *fbdefio;
29.#endif
30.
31. struct fb_ops *fbops;
32. struct device *device; /* This is the parent */
33. struct device *dev; /* This is this fb device */
34. int class_flag; /* private sysfs flags */
35.#ifdef CONFIG_FB_TILEBLITTING
36. struct fb_tile_ops *tileops; /* Tile Blitting */
37.#endif
38. char __iomem *screen_base; /* Virtual address */
39. unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
40. void *pseudo_palette; /* Fake palette of 16 colors */
41.#define FBINFO_STATE_RUNNING 0
42.#define FBINFO_STATE_SUSPENDED 1
43. u32 state; /* Hardware state i.e suspend */
44. void *fbcon_par; /* fbcon use-only private area */
45. /* From here on everything is device dependent */
46. void *par;
47. /* we need the PCI or similiar aperture base/size not
48. smem_start/size as smem_start may just be an object
49. allocated inside the aperture so may not actually overlap */
50. resource_size_t aperture_base;
51. resource_size_t aperture_size;
52.};
这个结构是frambuffer驱动的基本数据结构,里面包含了帧缓存设备的所有信息,每一个注册成frambuffer接口的设备都应该声明并初始化这样一个结构。register_framebuffer 函数的参数就是这样一个结构,fb_info在mini2440lcd驱动中是在s3c24xxfb_probe函数中分配并初始化的。其中struct fb_var_screeninfo结构包含了lcd显示中可以改变的信息,结构如下:
1.struct fb_var_screeninfo {
2. __u32 xres; /* 视口水平分辨率 */
3. __u32 yres;
4. __u32 xres_virtual; /* 虚拟屏幕水平分辨率 */
5. __u32 yres_virtual;
6. __u32 xoffset; /* 视口与虚拟屏幕水平分辨率偏移 */
7. __u32 yoffset;
8.
9.
10. __u32 bits_per_pixel; /* 像素的位数 */
11. __u32 grayscale; /* 灰度标志,如果为1代表是灰度 */
12.
13.
14. struct fb_bitfield red; /* 如果是真彩色,这个是颜色位,如果不是那么只有结构的大小重要,其他表示的信息无关紧要 */
15. struct fb_bitfield green;
16. struct fb_bitfield blue;
17. struct fb_bitfield transp; /* 透明度 */
18.
19.
20. __u32 nonstd; /* 非标准颜色表示标志位 */
21. __u32 activate; /* 参照 FB_ACTIVATE_* */
22. __u32 height; /* 在内存地址空间的长度 */
23. __u32 width; /* 在内存地址空间的宽度 */
24.
25.
26. __u32 accel_flags; /* (不用了) 参照 fb_info.flags */
27.
28.
29. /* 时序: 以下所有的值单位都是pixclock, 当然除了pixclock */
30. __u32 pixclock; /* 每秒像素值 */
31. __u32 left_margin; /* 从sync信号到显示真正的像素的时钟个数 */
32. __u32 right_margin; /* 从真正显示像素到sync信号的时钟个数 */
33. __u32 upper_margin; /* 上面两个是针对列像素的,这个针对行的 */
34. __u32 lower_margin;
35. __u32 hsync_len; /* 水平sync信号的长度 */
36. __u32 vsync_len; /* 垂直sync信号的长度 */
37. __u32 sync; /* 参照 FB_SYNC_* */
38. __u32 vmode; /* 参照 FB_VMODE_* */
39. __u32 rotate; /* angle we rotate counter clockwise */
40. __u32 reserved[5]; /* 保留 */
41.};
fb_fix_screeninfo包含了lcd显示中不可改变的信息,结构如下:
1.struct fb_fix_screeninfo {
2. char id[16]; /* 身份表示符,例如 "TT Builtin" */
3. unsigned long smem_start; /* frame buffer内存的开始地址 */
4. /* (物理地址) */
5. __u32 smem_len; /* frame buffer内存地址的长度 */
6. __u32 type; /* 参照 FB_TYPE_* */
7. __u32 type_aux; /* Interleave for interleaved Planes */
8. __u32 visual; /* 参照 FB_VISUAL_* */
9. __u16 xpanstep; /* zero if no hardware panning */
10. __u16 ypanstep; /* zero if no hardware panning */
11. __u16 ywrapstep; /* zero if no hardware ywrap */
12. __u32 line_length; /* 每行的长度,单位字节 */
13. unsigned long mmio_start; /* I/O 内存的开始地址 */
14. /* (物理地址) */
15. __u32 mmio_len; /* I/O内存的长度 */
16. __u32 accel; /* 对驱动程序的标示:是哪个设备*/
17. __u16 reserved[3]; /* 保留 */
18.};
其中倒数第三个成员par是设备自定义数据结构。在mini2440lcd驱动中为s3c2410fb_info,结构如下:
1.struct s3c2410fb_info {
2. struct device *dev;
3. struct clk *clk;
4.
5. struct resource *mem; //io内存物理地址也就是寄存器的地址
6. void __iomem *io; //用ioremap映射的io虚拟地址
7. void __iomem *irq_base; //中断控制器寄存器对应的虚拟地址
8.
9. enum s3c_drv_type drv_type;
10. struct s3c2410fb_hw regs;
11.
12. unsigned long clk_rate;
13. unsigned int palette_ready;
14.
15.#ifdef CONFIG_CPU_FREQ
16. struct notifier_block freq_transition;
17.#endif
18.
19. /* keep these registers in case we need to re-write palette */
20. u32 palette_buffer[256];
21. u32 pseudo_pal[16];
22.};
这个结构是和硬件相关的,包括寄存器的物理地址,虚拟地址和调色板的一些信息。这个结构也是在s3c24xxfb_probe中分配并初始化。
2. static struct fb_ops 结构
在mini2440lcd驱动中,fb_ops的初始化代码如下:
1.static struct fb_ops s3c2410fb_ops = {
2. .owner = THIS_MODULE,
3. .fb_check_var = s3c2410fb_check_var,
4. .fb_set_par = s3c2410fb_set_par,
5. .fb_blank = s3c2410fb_blank,
6. .fb_setcolreg = s3c2410fb_setcolreg,
7. .fb_fillrect = cfb_fillrect,
8. .fb_copyarea = cfb_copyarea,
9. .fb_imageblit = cfb_imageblit,
10.};
这些函数是驱动程序必须实现的,他们实现的功能对应frambuffer核心的Ioctl系统调用,当应用程序调用ioctl系统调用的时候,他们会被直接或间接的调用。其中:
s3c2410fb_check_var 和s3c2410fb_set_par会由fb_set_var调用,对应Ioctl的FBIOPUT_VSCREENINFO命令
s3c2410fb_blank ,对应ioctl的FBIOBLANK命令,其他几个函数也是类似。
3. struct s3c2410fb_mach_info 结构
1.struct s3c2410fb_mach_info {
2. struct s3c2410fb_display *displays; /* attached diplays info */
3. unsigned num_displays; /* number of defined displays */
4. unsigned default_display;
5. /* GPIOs */
6. unsigned long gpcup;
7. unsigned long gpcup_mask;
8. unsigned long gpccon;
9. unsigned long gpccon_mask;
10. unsigned long gpdup;
11. unsigned long gpdup_mask;
12. unsigned long gpdcon;
13. unsigned long gpdcon_mask;
14.
15. /* lpc3600 control register */
16. unsigned long lpcsel;
17.};
这个结构包括一个s3c2410fb_display结构体,其他的域是GPIO寄存器的信息。mini2440lcd驱动中定义并初始化了这样一个结构体:
1.static struct s3c2410fb_mach_info mini2440_fb_info __initdata = {
2. .displays = &mini2440_lcd_cfg,
3. .num_displays = 1,
4. .default_display = 0,
5.
6. .gpccon = 0xaa955699,
7. .gpccon_mask = 0xffc003cc,
8. .gpcup = 0x0000ffff,
9. .gpcup_mask = 0xffffffff,
10.
11. .gpdcon = 0xaa95aaa1,
12. .gpdcon_mask = 0xffc0fff0,
13. .gpdup = 0x0000faff,
14. .gpdup_mask = 0xffffffff,
15.
16. .lpcsel = 0xf82,
17.};
这里初始化了结构中的所有成员,s3c2410fb_display结构初始化成mini2440_lcd_cfg,这个结构的初始化是在/arch/arm/mach-s3c2440/mach-mini2440.c这个文件中。这里设置了s3c2440 lcd控制器对应的GPIO寄存器的初始值,在s3c2410fb_init_registers函数中将这些值写到相应的寄存器中。
4. s3c2410fb_display 结构
1.struct s3c2410fb_display {
2. /* LCD type */
3. unsigned type;
4.
5. /* Screen size */
6. unsigned short width;
7. unsigned short height;
8.
9. /* Screen info */
10. unsigned short xres;
11. unsigned short yres;
12. unsigned short bpp;
13.
14. unsigned pixclock; /* pixclock in picoseconds */
15. unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */
16. unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
17. unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */
18. unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */
19. unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */
20. unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */
21.
22. /* lcd configuration registers */
23. unsigned long lcdcon5;
24.};
这个结构体非常重要,他包括了一个lcd显示的所有必须的配置信息。程序就是用这个结构体初始化fb_info结构中的fb_var_screeninfo相关成员的。最后这些值都会写进lcd控制器的相应寄存器中。如上分析,这个结构在mini2440lcd驱动中被初始化成了mini2440_lcd_cfg,他定义在/arch/arm/mach-s3c2440/mach-mini2440.c,如下所示:
1.static struct s3c2410fb_display mini2440_lcd_cfg __initdata = {
2.
3.#if !defined (LCD_CON5)
4. .lcdcon5 = S3C2410_LCDCON5_FRM565 |
5. S3C2410_LCDCON5_INVVLINE |
6. S3C2410_LCDCON5_INVVFRAME |
7. S3C2410_LCDCON5_PWREN |
8. S3C2410_LCDCON5_HWSWP,
9.#else
10. .lcdcon5 = LCD_CON5,
11.#endif
12. .type = S3C2410_LCDCON1_TFT,
13. .width = LCD_WIDTH,
14. .height = LCD_HEIGHT,
15. .pixclock = LCD_PIXCLOCK,
16. .xres = LCD_WIDTH,
17. .yres = LCD_HEIGHT,
18. .bpp = 16,
19. .left_margin = LCD_LEFT_MARGIN + 1,
20. .right_margin = LCD_RIGHT_MARGIN + 1,
21. .hsync_len = LCD_HSYNC_LEN + 1,
22. .upper_margin = LCD_UPPER_MARGIN + 1,
23. .lower_margin = LCD_LOWER_MARGIN + 1,
24. .vsync_len = LCD_VSYNC_LEN + 1,
25.}
二. 模块初始化
1.s3c2410fb.c是内核的一个模块,在模块初始化函数中只是简单的调用了platform_driver_register把自己注册成为platform驱动。初始化函数如下:
1.int __init s3c2410fb_init(void)
2.{
3. int ret = platform_driver_register(&s3c2410fb_driver);
4.
5. if (ret == 0)
6. ret = platform_driver_register(&s3c2412fb_driver);
7.
8. return ret;
9.}
platform_driver_register是platform类型驱动的注册函数,他传入一个platform_driver结构体。mini2440lcd驱动初始化了这样一个结构体。如下:
1.static struct platform_driver s3c2410fb_driver = {
2. .probe = s3c2410fb_probe,
3. .remove = s3c2410fb_remove,
4. .suspend = s3c2410fb_suspend,
5. .resume = s3c2410fb_resume,
6. .driver = {
7. .name = "s3c2410-lcd",
8. .owner = THIS_MODULE,
9. },
10.};
可以看出这里初始化了相应的函数,以及设备名称和拥有模块。其中 name="s3c2410-lcd"这个很重要,他是platform核心寻找相应platform设备的的依据。初始化的各个函数都需要lcd驱动程序编写。
三. linux设备模型相关函数
1. 对应上面的platform_driver初始化用的函数:
s3c2410fb_probe
s3c2410fb_remove
s3c2410fb_suspend,
s3c2410fb_resume,
其中s3c2410fb_probe函数是调用platform_driver_register时,由platform_bus的match函数找到合适的lcd设备成功后调用的函数,完成初始化工作。下面重点分析这个函数。
2. s3c2410fb_probe 函数分析
这个函数只有一条语句就是调用s3c24xxfb_probe,下面是s3c24xxfb_probe函数,这个是lcd驱动最关键的函数。
1.static int __init s3c24xxfb_probe(struct platform_device *pdev,
2. enum s3c_drv_type drv_type)
3.{
4. struct s3c2410fb_info *info;
5. struct s3c2410fb_display *display;
6. struct fb_info *fbinfo;
7. struct s3c2410fb_mach_info *mach_info;
8. struct resource *res;
9. int ret;
10. int irq;
11. int i;
12. int size;
13. u32 lcdcon1;
14.
15. mach_info = pdev->dev.platform_data;
16. //在/arch/arm/mach-s3c2440/mach-mini2440.c的mini2440_machine_init函数中,调用s3c24xx_fb_set_platdata(&mini2440_fb_info)
17. //将mini2440_fb_info赋值给pdev->dev.paltform_data,所以这里得到的是mini2440_fb_info
18. if (mach_info == NULL) {
19. dev_err(&pdev->dev,
20. "no platform data for lcd, cannot attach\n");
21. return -EINVAL;
22. }
23.
24. if (mach_info->default_display >= mach_info->num_displays) {
25. dev_err(&pdev->dev, "default is %d but only %d displays\n",
26. mach_info->default_display, mach_info->num_displays);
27. return -EINVAL;
28. }
29.
30. display = mach_info->displays + mach_info->default_display;
31. //mach_info->displays = 0,mach_info->default_display = mini2440_lcd_cfg
32. //所以display = mini2440_lcd_cfg
33. irq = platform_get_irq(pdev, 0);
34. //pdev是platfoem_device结构,这个函数是从platform_device占用的资源里取出irq号
35. if (irq < 0) {
36. dev_err(&pdev->dev, "no irq for device\n");
37. return -ENOENT;
38. }
39.
40. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
41. //framebuffer_alloc所做的事就是分配一个fb_info结构体,因为这个结构体最后有个通用指针*par,这个是设备自定义结构,在这里是s3c24fb_info
42. //所以分配内存的时候在fb_info结构的大小基础上必须加上s3c2410fb_info结构的大小,这样才是这里的fb_info真正的大小
43. if (!fbinfo)
44. return -ENOMEM;
45.
46. platform_set_drvdata(pdev, fbinfo);
47.
48. info = fbinfo->par; //将info(s3c2410fb_info结构)指向新分配的fbinfo的par位置
49. info->dev = &pdev->dev;
50. info->drv_type = drv_type;
51.
52. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
53. //得到lcd控制器io内存的物理地址
54. if (res == NULL) {
55. dev_err(&pdev->dev, "failed to get memory registers\n");
56. ret = -ENXIO;
57. goto dealloc_fb;
58. }
59.
60. size = (res->end - res->start) + 1;
61. info->mem = request_mem_region(res->start, size, pdev->name);
62. //向内核请求所用的io内存,这里主要防止其他模块竞争,如果其他模块占用这块内存,函数就会返回NULL
63. if (info->mem == NULL) {
64. dev_err(&pdev->dev, "failed to get memory region\n");
65. ret = -ENOENT;
66. goto dealloc_fb;
67. }
68.
69. info->io = ioremap(res->start, size);
70. //将物理内存映射成虚拟地址,以供内核使用
71. if (info->io == NULL) {
72. dev_err(&pdev->dev, "ioremap() of registers failed\n");
73. ret = -ENXIO;
74. goto release_mem;
75. }
76.
77. info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
78. // irq_base是lcd中断控制器寄存器对应的虚拟地址
79. dprintk("devinit\n");
80.
81. strcpy(fbinfo->fix.id, driver_name);
82.
83. /* Stop the video */
84. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
85. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
86.
87. fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
88. fbinfo->fix.type_aux = 0;
89. fbinfo->fix.xpanstep = 0;
90. fbinfo->fix.ypanstep = 0;
91. fbinfo->fix.ywrapstep = 0;
92. fbinfo->fix.accel = FB_ACCEL_NONE;
93. //以上初始化fb_fix_screeninfo结构
94. fbinfo->var.nonstd = 0;
95. fbinfo->var.activate = FB_ACTIVATE_NOW;
96. fbinfo->var.accel_flags = 0;
97. fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
98. //以上初始化fb_var_screeninfo结构
99. fbinfo->fbops = &s3c2410fb_ops;
100. // 这里将我们实现的函数与frambuffer核心的操作联系上
101. fbinfo->flags = FBINFO_FLAG_DEFAULT;
102. fbinfo->pseudo_palette = &info->pseudo_pal;
103.
104. for (i = 0; i < 256; i++)
105. info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
106.
107. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
108. //注册中断处理函数,一般的lcd操作基本不需要中断
109. if (ret) {
110. dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
111. ret = -EBUSY;
112. goto release_regs;
113. }
114.
115. info->clk = clk_get(NULL, "lcd");
116. if (!info->clk || IS_ERR(info->clk)) {
117. printk(KERN_ERR "failed to get lcd clock source\n");
118. ret = -ENOENT;
119. goto release_irq;
120. }
121.
122. clk_enable(info->clk);
123. //以上操作使能lcd时钟
124. dprintk("got and enabled clock\n");
125.
126. msleep(1);
127.
128. info->clk_rate = clk_get_rate(info->clk);
129. /* find maximum required memory size for display */
130. for (i = 0; i < mach_info->num_displays; i++) {
131. unsigned long smem_len = mach_info->displays[i].xres; // = 240
132.
133. smem_len *= mach_info->displays[i].yres; // = 320
134. smem_len *= mach_info->displays[i].bpp; // = 16
135. smem_len >>= 3; //将位的个数转换成字节个数
136. if (fbinfo->fix.smem_len < smem_len)
137. fbinfo->fix.smem_len = smem_len;
138. }
139.
140. /* Initialize video memory */
141. ret = s3c2410fb_map_video_memory(fbinfo);
142. //这个函数主要功能就是分配一块内存,大小为上面计算的smem_len,并且将分配的内存的物理地址赋值给fbinfo->fix.smem_start
143. //将虚拟地址赋值给fbinfo->screen_base
144. if (ret) {
145. printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
146. ret = -ENOMEM;
147. goto release_clock;
148. }
149.
150. dprintk("got video memory\n");
151.
152. fbinfo->var.xres = display->xres;
153. fbinfo->var.yres = display->yres;
154. fbinfo->var.bits_per_pixel = display->bpp;
155. //这三个初始化很重要,对于下面的s3c2410fb_check_var尤其重要
156. s3c2410fb_init_registers(fbinfo);
157. //初始化lcd控制器的GPIO接口控制寄存器
158.
159. s3c2410fb_check_var(&fbinfo->var, fbinfo);
160. //这个函数根据fbinfo->var的xres,yres和bits_per_pixel选择相应的s3c2410fb_display结构,并将这个结构的各个域的值赋值给
161. //fbinfo->var的相应成员。因为mini2440lcd驱动只有一个s3c2410fb_display结构就是mini2440_lcd_cfg,所以赋值的就是mini2440_lcd_cfg
162. ret = s3c2410fb_cpufreq_register(info);
163. if (ret < 0) {
164. dev_err(&pdev->dev, "Failed to register cpufreq\n");
165. goto free_video_memory;
166. }
167.
168. ret = register_framebuffer(fbinfo);
169. //将fbinfo结构注册到frambuffer核心
170. if (ret < 0) {
171. printk(KERN_ERR "Failed to register framebuffer device: %d\n",
172. ret);
173. goto free_cpufreq;
174. }
175.
176. /* create device files */
177. ret = device_create_file(&pdev->dev, &dev_attr_debug);
178. if (ret) {
179. printk(KERN_ERR "failed to add debug attribute\n");
180. }
181.
182. printk(KERN_INFO "fb%d: %s frame buffer device\n",
183. fbinfo->node, fbinfo->fix.id);
184.
185. return 0;
186.
187.free_cpufreq:
188. s3c2410fb_cpufreq_deregister(info);
189.free_video_memory:
190. s3c2410fb_unmap_video_memory(fbinfo);
191.release_clock:
192. clk_disable(info->clk);
193. clk_put(info->clk);
194.release_irq:
195. free_irq(irq, info);
196.release_regs:
197. iounmap(info->io);
198.release_mem:
199. release_resource(info->mem);
200. kfree(info->mem);
201.dealloc_fb:
202. platform_set_drvdata(pdev, NULL);
203. framebuffer_release(fbinfo);
204. return ret;
205.}
从上面分析可以看出,这个函数主要做了下面几件事:
(1) 从platform_device中获得s3c2410fb_mach_info结构体赋值给mach_info。这就得到了lcd控制器的所有初始配置。
(2) 从mach_info中获得s3c2410fb_display结构体赋值给display。这样就得到了显示相关的初始配置。
(3) 分配一个fb_info结构体fbinfo和一个s3c2410fb_info结构体info,并且将info指向fbinfo->par
(4) 由pdev中所用的资源初始化info结构,主要初始化io内存,并映射虚拟地址。
(5) 关闭lcd显示
(6) 初始化fbinfo->fix,fbinfo->var 的部分域(不依赖配置信息的部分)
(7) 初始化fbinfo->fbops为s3c2410fb_ops
(8) 注册中断处理程序s3c2410fb_irq
(9) 使能lcd时钟
(10)为lcd设备分配显存,显存开始地址赋值给fbinfo->screen_base
(11)初始化lcd控制器的io接口控制寄存器
(12)用display中的值初始化fbinfo->var中相应的值(与显示配置相关的部分)
(13)将fbinfo结构注册到frambuffer核心
四. frambuffer驱动模型fb_ops各函数的实现
由s3c2410fb_ops结构可以看出,mini2440lcd驱动主要实现了下面几个函数:
s3c2410fb_check_var
s3c2410fb_set_par
s3c2410fb_blank
s3c2410fb_setcolreg
1.s3c2410fb.c是内核的一个模块,在模块初始化函数中只是简单的调用了platform_driver_register把自己注册成为platform驱动。初始化函数如下:
1.int __init s3c2410fb_init(void)
2.{
3. int ret = platform_driver_register(&s3c2410fb_driver);
4.
5. if (ret == 0)
6. ret = platform_driver_register(&s3c2412fb_driver);
7.
8. return ret;
9.}
platform_driver_register是platform类型驱动的注册函数,他传入一个platform_driver结构体。mini2440lcd驱动初始化了这样一个结构体。如下:
1.static struct platform_driver s3c2410fb_driver = {
2. .probe = s3c2410fb_probe,
3. .remove = s3c2410fb_remove,
4. .suspend = s3c2410fb_suspend,
5. .resume = s3c2410fb_resume,
6. .driver = {
7. .name = "s3c2410-lcd",
8. .owner = THIS_MODULE,
9. },
10.};
可以看出这里初始化了相应的函数,以及设备名称和拥有模块。其中 name="s3c2410-lcd"这个很重要,他是platform核心寻找相应platform设备的的依据。初始化的各个函数都需要lcd驱动程序编写。
三. linux设备模型相关函数
1. 对应上面的platform_driver初始化用的函数:
s3c2410fb_probe
s3c2410fb_remove
s3c2410fb_suspend,
s3c2410fb_resume,
其中s3c2410fb_probe函数是调用platform_driver_register时,由platform_bus的match函数找到合适的lcd设备成功后调用的函数,完成初始化工作。下面重点分析这个函数。
2. s3c2410fb_probe 函数分析
这个函数只有一条语句就是调用s3c24xxfb_probe,下面是s3c24xxfb_probe函数,这个是lcd驱动最关键的函数。
1.static int __init s3c24xxfb_probe(struct platform_device *pdev,
2. enum s3c_drv_type drv_type)
3.{
4. struct s3c2410fb_info *info;
5. struct s3c2410fb_display *display;
6. struct fb_info *fbinfo;
7. struct s3c2410fb_mach_info *mach_info;
8. struct resource *res;
9. int ret;
10. int irq;
11. int i;
12. int size;
13. u32 lcdcon1;
14.
15. mach_info = pdev->dev.platform_data;
16. //在/arch/arm/mach-s3c2440/mach-mini2440.c的mini2440_machine_init函数中,调用s3c24xx_fb_set_platdata(&mini2440_fb_info)
17. //将mini2440_fb_info赋值给pdev->dev.paltform_data,所以这里得到的是mini2440_fb_info
18. if (mach_info == NULL) {
19. dev_err(&pdev->dev,
20. "no platform data for lcd, cannot attach\n");
21. return -EINVAL;
22. }
23.
24. if (mach_info->default_display >= mach_info->num_displays) {
25. dev_err(&pdev->dev, "default is %d but only %d displays\n",
26. mach_info->default_display, mach_info->num_displays);
27. return -EINVAL;
28. }
29.
30. display = mach_info->displays + mach_info->default_display;
31. //mach_info->displays = 0,mach_info->default_display = mini2440_lcd_cfg
32. //所以display = mini2440_lcd_cfg
33. irq = platform_get_irq(pdev, 0);
34. //pdev是platfoem_device结构,这个函数是从platform_device占用的资源里取出irq号
35. if (irq < 0) {
36. dev_err(&pdev->dev, "no irq for device\n");
37. return -ENOENT;
38. }
39.
40. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
41. //framebuffer_alloc所做的事就是分配一个fb_info结构体,因为这个结构体最后有个通用指针*par,这个是设备自定义结构,在这里是s3c24fb_info
42. //所以分配内存的时候在fb_info结构的大小基础上必须加上s3c2410fb_info结构的大小,这样才是这里的fb_info真正的大小
43. if (!fbinfo)
44. return -ENOMEM;
45.
46. platform_set_drvdata(pdev, fbinfo);
47.
48. info = fbinfo->par; //将info(s3c2410fb_info结构)指向新分配的fbinfo的par位置
49. info->dev = &pdev->dev;
50. info->drv_type = drv_type;
51.
52. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
53. //得到lcd控制器io内存的物理地址
54. if (res == NULL) {
55. dev_err(&pdev->dev, "failed to get memory registers\n");
56. ret = -ENXIO;
57. goto dealloc_fb;
58. }
59.
60. size = (res->end - res->start) + 1;
61. info->mem = request_mem_region(res->start, size, pdev->name);
62. //向内核请求所用的io内存,这里主要防止其他模块竞争,如果其他模块占用这块内存,函数就会返回NULL
63. if (info->mem == NULL) {
64. dev_err(&pdev->dev, "failed to get memory region\n");
65. ret = -ENOENT;
66. goto dealloc_fb;
67. }
68.
69. info->io = ioremap(res->start, size);
70. //将物理内存映射成虚拟地址,以供内核使用
71. if (info->io == NULL) {
72. dev_err(&pdev->dev, "ioremap() of registers failed\n");
73. ret = -ENXIO;
74. goto release_mem;
75. }
76.
77. info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
78. // irq_base是lcd中断控制器寄存器对应的虚拟地址
79. dprintk("devinit\n");
80.
81. strcpy(fbinfo->fix.id, driver_name);
82.
83. /* Stop the video */
84. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
85. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
86.
87. fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
88. fbinfo->fix.type_aux = 0;
89. fbinfo->fix.xpanstep = 0;
90. fbinfo->fix.ypanstep = 0;
91. fbinfo->fix.ywrapstep = 0;
92. fbinfo->fix.accel = FB_ACCEL_NONE;
93. //以上初始化fb_fix_screeninfo结构
94. fbinfo->var.nonstd = 0;
95. fbinfo->var.activate = FB_ACTIVATE_NOW;
96. fbinfo->var.accel_flags = 0;
97. fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
98. //以上初始化fb_var_screeninfo结构
99. fbinfo->fbops = &s3c2410fb_ops;
100. // 这里将我们实现的函数与frambuffer核心的操作联系上
101. fbinfo->flags = FBINFO_FLAG_DEFAULT;
102. fbinfo->pseudo_palette = &info->pseudo_pal;
103.
104. for (i = 0; i < 256; i++)
105. info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
106.
107. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
108. //注册中断处理函数,一般的lcd操作基本不需要中断
109. if (ret) {
110. dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
111. ret = -EBUSY;
112. goto release_regs;
113. }
114.
115. info->clk = clk_get(NULL, "lcd");
116. if (!info->clk || IS_ERR(info->clk)) {
117. printk(KERN_ERR "failed to get lcd clock source\n");
118. ret = -ENOENT;
119. goto release_irq;
120. }
121.
122. clk_enable(info->clk);
123. //以上操作使能lcd时钟
124. dprintk("got and enabled clock\n");
125.
126. msleep(1);
127.
128. info->clk_rate = clk_get_rate(info->clk);
129. /* find maximum required memory size for display */
130. for (i = 0; i < mach_info->num_displays; i++) {
131. unsigned long smem_len = mach_info->displays[i].xres; // = 240
132.
133. smem_len *= mach_info->displays[i].yres; // = 320
134. smem_len *= mach_info->displays[i].bpp; // = 16
135. smem_len >>= 3; //将位的个数转换成字节个数
136. if (fbinfo->fix.smem_len < smem_len)
137. fbinfo->fix.smem_len = smem_len;
138. }
139.
140. /* Initialize video memory */
141. ret = s3c2410fb_map_video_memory(fbinfo);
142. //这个函数主要功能就是分配一块内存,大小为上面计算的smem_len,并且将分配的内存的物理地址赋值给fbinfo->fix.smem_start
143. //将虚拟地址赋值给fbinfo->screen_base
144. if (ret) {
145. printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
146. ret = -ENOMEM;
147. goto release_clock;
148. }
149.
150. dprintk("got video memory\n");
151.
152. fbinfo->var.xres = display->xres;
153. fbinfo->var.yres = display->yres;
154. fbinfo->var.bits_per_pixel = display->bpp;
155. //这三个初始化很重要,对于下面的s3c2410fb_check_var尤其重要
156. s3c2410fb_init_registers(fbinfo);
157. //初始化lcd控制器的GPIO接口控制寄存器
158.
159. s3c2410fb_check_var(&fbinfo->var, fbinfo);
160. //这个函数根据fbinfo->var的xres,yres和bits_per_pixel选择相应的s3c2410fb_display结构,并将这个结构的各个域的值赋值给
161. //fbinfo->var的相应成员。因为mini2440lcd驱动只有一个s3c2410fb_display结构就是mini2440_lcd_cfg,所以赋值的就是mini2440_lcd_cfg
162. ret = s3c2410fb_cpufreq_register(info);
163. if (ret < 0) {
164. dev_err(&pdev->dev, "Failed to register cpufreq\n");
165. goto free_video_memory;
166. }
167.
168. ret = register_framebuffer(fbinfo);
169. //将fbinfo结构注册到frambuffer核心
170. if (ret < 0) {
171. printk(KERN_ERR "Failed to register framebuffer device: %d\n",
172. ret);
173. goto free_cpufreq;
174. }
175.
176. /* create device files */
177. ret = device_create_file(&pdev->dev, &dev_attr_debug);
178. if (ret) {
179. printk(KERN_ERR "failed to add debug attribute\n");
180. }
181.
182. printk(KERN_INFO "fb%d: %s frame buffer device\n",
183. fbinfo->node, fbinfo->fix.id);
184.
185. return 0;
186.
187.free_cpufreq:
188. s3c2410fb_cpufreq_deregister(info);
189.free_video_memory:
190. s3c2410fb_unmap_video_memory(fbinfo);
191.release_clock:
192. clk_disable(info->clk);
193. clk_put(info->clk);
194.release_irq:
195. free_irq(irq, info);
196.release_regs:
197. iounmap(info->io);
198.release_mem:
199. release_resource(info->mem);
200. kfree(info->mem);
201.dealloc_fb:
202. platform_set_drvdata(pdev, NULL);
203. framebuffer_release(fbinfo);
204. return ret;
205.}
从上面分析可以看出,这个函数主要做了下面几件事:
(1) 从platform_device中获得s3c2410fb_mach_info结构体赋值给mach_info。这就得到了lcd控制器的所有初始配置。
(2) 从mach_info中获得s3c2410fb_display结构体赋值给display。这样就得到了显示相关的初始配置。
(3) 分配一个fb_info结构体fbinfo和一个s3c2410fb_info结构体info,并且将info指向fbinfo->par
(4) 由pdev中所用的资源初始化info结构,主要初始化io内存,并映射虚拟地址。
(5) 关闭lcd显示
(6) 初始化fbinfo->fix,fbinfo->var 的部分域(不依赖配置信息的部分)
(7) 初始化fbinfo->fbops为s3c2410fb_ops
(8) 注册中断处理程序s3c2410fb_irq
(9) 使能lcd时钟
(10)为lcd设备分配显存,显存开始地址赋值给fbinfo->screen_base
(11)初始化lcd控制器的io接口控制寄存器
(12)用display中的值初始化fbinfo->var中相应的值(与显示配置相关的部分)
(13)将fbinfo结构注册到frambuffer核心
四. frambuffer驱动模型fb_ops各函数的实现
由s3c2410fb_ops结构可以看出,mini2440lcd驱动主要实现了下面几个函数:
s3c2410fb_check_var
s3c2410fb_set_par
s3c2410fb_blank
s3c2410fb_setcolreg
cfb_fillrect
cfb_copyarea
cfb_imageblit
其中最重要的是s3c2410fb_set_par,这个函数根据fbinfo的值初始化了底层的lcd控制器,重点分析这个函数。他由fb_set_var调用,对应则frambufer核心ioctl中的FBIOPUT_VSCREENINFO命令。其他的函数也是为了完成lcd的相关功能而编写的,与具体实现的功能有关。s3c2410fb_set_par函数定义如下:
1.static int s3c2410fb_set_par(struct fb_info *info)
2.{
3. struct fb_var_screeninfo *var = &info->var;
4.
5. switch (var->bits_per_pixel) {
6. case 32:
7. case 16:
8. case 12:
9. info->fix.visual = FB_VISUAL_TRUECOLOR;
10. break;
11. case 1:
12. info->fix.visual = FB_VISUAL_MONO01;
13. break;
14. default:
15. info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
16. break;
17. }
18.
19. info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
20. /* activate this new configuration */
21. s3c2410fb_activate_var(info);
22. return 0;
23.}
可以看出,这个函数除了根据fbinfo的像素位来赋值fix.visual外,主要是调用了s3c2410fb_activate_var函数:
1.static void s3c2410fb_activate_var(struct fb_info *info)
2.{
3. struct s3c2410fb_info *fbi = info->par;
4. void __iomem *regs = fbi->io;
5. int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
6. struct fb_var_screeninfo *var = &info->var;
7. int clkdiv;
8.
9. clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
10.
11. dprintk("%s: var->xres = %d\n", __func__, var->xres);
12. dprintk("%s: var->yres = %d\n", __func__, var->yres);
13. dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
14.
15. if (type == S3C2410_LCDCON1_TFT) {
16. //判断lcd型号,我们的lcd是TFT型lcd,所以下面语句执行
17. s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
18. //这个函数主要的功能就是将info中的lcd配置相关的值赋值给s3c2410fb_info结构的regs成员
19. //这个regs是一个s3c2410fb_hw结构,这个结构就是定义了5个lcd控制寄存器lcdcon1~5
20. --clkdiv;
21. if (clkdiv < 0)
22. clkdiv = 0;
23. } else {
24. //如果是STN型的lcd,那么执行下面的函数。因为s3c2440的lcd控制器由有几个专门用于控制STN型lcd的寄存器,所以要单独设置
25. s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
26. if (clkdiv < 2)
27. clkdiv = 2;
28. }
29.
30. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
31. /* write new registers */
32. dprintk("new register set:\n");
33. dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
34. dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
35. dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
36. dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
37. dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
38.
39. writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
40. regs + S3C2410_LCDCON1);
41. writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
42. writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
43. writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
44. writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
45. //将配置值写入五个寄存器
46. /* set lcd address pointers */
47. s3c2410fb_set_lcdaddr(info);
48. //设置显存地址寄存器,设置为我们分配的那块内存,设置之后,lcd控制器就会在这块内存取数据送往lcd显示
49.
50. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
51. //打开视频显示,这样lcd就可以正确显示了
52. writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
53.}
五. 总结
mini2440lcd驱动分别涉及到了platform和frambuffer核心,利用这两个核心实现其功能。刚开始分析这个驱动的时候,总是感觉很乱没有重点。我想这个主要是和自己对frambuffer和lcd显示还不熟悉的原因。看了几天过后才逐渐有点眉目。最让人迷惑的就是如何设置lcd控制器的寄存器问题。我以前认为这个应该在probe函数中设置,一般来说这个函数检测设备状态,初始化设备,然后设备就绪,应用程序就可以操作了。但是在s3c2410fb_probe中只是设置了相关的GPIO接口寄存器,根本没有设置lcd控制寄存器。后来发现是在s3c2410fb_set_par函数中设置的。这个函数对应用户空间ioctl的FBIOPUT_VSCREENINFO。也就是说应用程序必须调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)才能设置正确的lcd状态,但是这个命令有一个参数是fb_var_screeninfo结构,也就意味这应用程序必须填充这样一个结构,才可以调用ioctl。这样一来内核初始化的默认配置信息就没用了。唯一的办法是先调用ioctl(fd,FBIOGET_VSCREENINFO,struct fb_var_screeninfo *var)获得这个结构,然后修改之后在调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)将修改的值写入。我在MiniGUI的源码中验证了这个推论,在MiniGUI的fbcon图形引擎中的FB_SetVideoMode函数中,有如下的调用
1.if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
2. GAL_SetError("NEWGAL>FBCON: Couldn't get console pixel format\n");
3. FB_VideoQuit(this);
4. return(-1);
5.}
然后就是设置finfo中需要改变的值,最后有下面的代码来设置lcd控制寄存器器
1.if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
2. vinfo.yres_virtual = height;
3. if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
4. GAL_SetError("NEWGAL>FBCON: Couldn't set console screen info");
5. return(NULL);
6. }
7.}
关于lcd驱动还有好多知识要学,比如mmap操作。mmap是一般lcd应用程序运行的模式,他将显存映射到用户空间,提高系统的性能。因为自己只是为了熟悉一下这个驱动,所以没有深入看下去。
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-10/44431p3.htm
cfb_copyarea
cfb_imageblit
其中最重要的是s3c2410fb_set_par,这个函数根据fbinfo的值初始化了底层的lcd控制器,重点分析这个函数。他由fb_set_var调用,对应则frambufer核心ioctl中的FBIOPUT_VSCREENINFO命令。其他的函数也是为了完成lcd的相关功能而编写的,与具体实现的功能有关。s3c2410fb_set_par函数定义如下:
1.static int s3c2410fb_set_par(struct fb_info *info)
2.{
3. struct fb_var_screeninfo *var = &info->var;
4.
5. switch (var->bits_per_pixel) {
6. case 32:
7. case 16:
8. case 12:
9. info->fix.visual = FB_VISUAL_TRUECOLOR;
10. break;
11. case 1:
12. info->fix.visual = FB_VISUAL_MONO01;
13. break;
14. default:
15. info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
16. break;
17. }
18.
19. info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
20. /* activate this new configuration */
21. s3c2410fb_activate_var(info);
22. return 0;
23.}
可以看出,这个函数除了根据fbinfo的像素位来赋值fix.visual外,主要是调用了s3c2410fb_activate_var函数:
1.static void s3c2410fb_activate_var(struct fb_info *info)
2.{
3. struct s3c2410fb_info *fbi = info->par;
4. void __iomem *regs = fbi->io;
5. int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
6. struct fb_var_screeninfo *var = &info->var;
7. int clkdiv;
8.
9. clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
10.
11. dprintk("%s: var->xres = %d\n", __func__, var->xres);
12. dprintk("%s: var->yres = %d\n", __func__, var->yres);
13. dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
14.
15. if (type == S3C2410_LCDCON1_TFT) {
16. //判断lcd型号,我们的lcd是TFT型lcd,所以下面语句执行
17. s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
18. //这个函数主要的功能就是将info中的lcd配置相关的值赋值给s3c2410fb_info结构的regs成员
19. //这个regs是一个s3c2410fb_hw结构,这个结构就是定义了5个lcd控制寄存器lcdcon1~5
20. --clkdiv;
21. if (clkdiv < 0)
22. clkdiv = 0;
23. } else {
24. //如果是STN型的lcd,那么执行下面的函数。因为s3c2440的lcd控制器由有几个专门用于控制STN型lcd的寄存器,所以要单独设置
25. s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
26. if (clkdiv < 2)
27. clkdiv = 2;
28. }
29.
30. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
31. /* write new registers */
32. dprintk("new register set:\n");
33. dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
34. dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
35. dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
36. dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
37. dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
38.
39. writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
40. regs + S3C2410_LCDCON1);
41. writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
42. writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
43. writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
44. writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
45. //将配置值写入五个寄存器
46. /* set lcd address pointers */
47. s3c2410fb_set_lcdaddr(info);
48. //设置显存地址寄存器,设置为我们分配的那块内存,设置之后,lcd控制器就会在这块内存取数据送往lcd显示
49.
50. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
51. //打开视频显示,这样lcd就可以正确显示了
52. writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
53.}
五. 总结
mini2440lcd驱动分别涉及到了platform和frambuffer核心,利用这两个核心实现其功能。刚开始分析这个驱动的时候,总是感觉很乱没有重点。我想这个主要是和自己对frambuffer和lcd显示还不熟悉的原因。看了几天过后才逐渐有点眉目。最让人迷惑的就是如何设置lcd控制器的寄存器问题。我以前认为这个应该在probe函数中设置,一般来说这个函数检测设备状态,初始化设备,然后设备就绪,应用程序就可以操作了。但是在s3c2410fb_probe中只是设置了相关的GPIO接口寄存器,根本没有设置lcd控制寄存器。后来发现是在s3c2410fb_set_par函数中设置的。这个函数对应用户空间ioctl的FBIOPUT_VSCREENINFO。也就是说应用程序必须调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)才能设置正确的lcd状态,但是这个命令有一个参数是fb_var_screeninfo结构,也就意味这应用程序必须填充这样一个结构,才可以调用ioctl。这样一来内核初始化的默认配置信息就没用了。唯一的办法是先调用ioctl(fd,FBIOGET_VSCREENINFO,struct fb_var_screeninfo *var)获得这个结构,然后修改之后在调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)将修改的值写入。我在MiniGUI的源码中验证了这个推论,在MiniGUI的fbcon图形引擎中的FB_SetVideoMode函数中,有如下的调用
1.if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
2. GAL_SetError("NEWGAL>FBCON: Couldn't get console pixel format\n");
3. FB_VideoQuit(this);
4. return(-1);
5.}
然后就是设置finfo中需要改变的值,最后有下面的代码来设置lcd控制寄存器器
1.if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
2. vinfo.yres_virtual = height;
3. if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
4. GAL_SetError("NEWGAL>FBCON: Couldn't set console screen info");
5. return(NULL);
6. }
7.}
关于lcd驱动还有好多知识要学,比如mmap操作。mmap是一般lcd应用程序运行的模式,他将显存映射到用户空间,提高系统的性能。因为自己只是为了熟悉一下这个驱动,所以没有深入看下去。
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-10/44431p3.htm
- mini2440 LCD驱动
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440驱动分析之LCD
- mini2440裸机编程--------LCD驱动
- mini2440驱动分析之LCD
- mini2440 X35 移植LCD驱动
- 在友善之臂mini2440上驱动LCD
- mini2440裸机程序 统宝3.5 LCD驱动 TopPoly-TD035STED4
- u-boot-2009.11在MINI2440上的移植 LCD驱动
- linux2.6.32.2 mini2440平台移植--LCD 背光驱动
- linux2.6.32.2 mini2440平台移植--LCD 背光驱动
- 2012这一年和那一年
- 【Visual C++】游戏开发笔记三十七 浅墨DirectX提高班之五 顶点缓存的红颜知己:索引缓存的故事
- 软件程序编码
- 如果我管理qq手机浏览器,如何振兴?
- Servlet 第一个DEMO
- mini2440 LCD驱动
- 【Visual C++】游戏开发笔记三十八 浅墨DirectX提高班之六 携手迈向三维世界:四大变换展身手
- WEB应用Excel报表加水印解决方案
- Bayesian Computation with R (Jim Albert) 学习笔记I
- 从spin_lock到spin_lock_irqsave
- linux下java多文件编译和jar打包
- tomcat中配置ssl
- 如何知道软件(如Lodop)很受欢迎?
- vim使用F5编译C语言程序