资源描述
Linux的LCD驱动源码分析及移植(三部曲)
第一部分:
基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.1(LCD驱动源码分析及移植之platform device)
1.与LCD控制器硬件相关的寄存器内容请参照三星S3C2440A技术手册中的第15章。
2. LCD Controller的平台设备定义如下(文件位于 linux/arch/arm/plat-s3c24xx/devs.c):
1. /* LCD Controller */
2.
3. static struct resource s3c_lcd_resource[] = {
4. [0] = {
5. .start = S3C24XX_PA_LCD,
6. .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
7. .flags = IORESOURCE_MEM,
8. },
9. [1] = {
10. .start = IRQ_LCD,
11. .end = IRQ_LCD,
12. .flags = IORESOURCE_IRQ,
13. }
14.
15. };
16.
17. static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
18.
19. struct platform_device s3c_device_lcd = {
20. .name = "s3c2410-lcd",
21. .id = -1,
22. .num_resources = ARRAY_SIZE(s3c_lcd_resource),
23. .resource = s3c_lcd_resource,
24. .dev = {
25. .dma_mask = &s3c_device_lcd_dmamask,
26. .coherent_dma_mask = 0xffffffffUL
27. }
28. };
29.
30. EXPORT_SYMBOL(s3c_device_lcd);
平台设备的结构体定义为s3c_device_lcd,该设备在平台总线中的名字取为s3c2410-lcd,该平台设备申请的两个板级资源为以S3C24XX_PA_LCD为起始的IORESOURCE_MEM资源和一个定义为IRQ_LCD的IORESOURCE_IRQ资源。
其中,
1. #define S3C24XX_PA_LCD S3C2410_PA_LCD
1. /* LCD controller */
2. #define S3C2410_PA_LCD (0x4D000000)
3. #define S3C24XX_SZ_LCD SZ_1M
0x4D000000为LCDCON1寄存器的地址。
3. LCD Controller的平台设备的注册如下(文件位于linux/arch/arm/mach-s3c2440/mach-smdk2440.c):
1. static struct platform_device *smdk2440_devices[] __initdata = {
2. &s3c_device_usb,
3.
4. &s3c_device_lcd,
5.
6. &s3c_device_wdt,
7. &s3c_device_i2c0,
8. &s3c_device_iis,
9. &s3c_device_rtc,
10. };
以上第4行代码将lcd平台设备注册进内核。
4.在系统初始化时将smdk2440_fb_info结构体添加进平台设备的私有结构中。具体流程如下:
4.1
1. MACHINE_START(S3C2440, "SMDK2440")
2. /* Maintainer: Ben Dooks <ben@fluff.org> */
3. .phys_io = S3C2410_PA_UART,
4. .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
5. .boot_params = S3C2410_SDRAM_PA + 0x100,
6.
7. .init_irq = s3c24xx_init_irq,
8. .map_io = smdk2440_map_io,
9. .init_machine = smdk2440_machine_init,
10. .timer = &s3c24xx_timer,
11. MACHINE_END
启动S3C2440机器,系统将通过“.init_machine = smdk2440_machine_init,”调用smdk2440_machine_init()函数。
4.2
1. static void __init smdk2440_machine_init(void)
2. {
3. s3c24xx_fb_set_platdata(&smdk2440_fb_info);
4. s3c_i2c0_set_platdata(NULL);
5.
6. platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
7. smdk_machine_init();
8. }
在 smdk2440_machine_init函数中,通过“ s3c24xx_fb_set_platdata(&smdk2440_fb_info);”将smdk2440_fb_info添加进平台设备的私有结构中。
4.3
1. void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
2. {
3. struct s3c2410fb_mach_info *npd;
4.
5. npd = kmalloc(sizeof(*npd), GFP_KERNEL);
6. if (npd) {
7. memcpy(npd, pd, sizeof(*npd));
8. s3c_device_lcd.dev.platform_data = npd;
9. } else {
10. printk(KERN_ERR "no memory for LCD platform data\n");
11. }
12. }
以上代码为smdk2440_fb_info结构的添加过程,其中“s3c_device_lcd.dev.platform_data = npd;”为核心实现部分。
4.4
1. static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
2. .displays = &smdk2440_lcd_cfg,
3. .num_displays = 1,
4. .default_display = 0,
5.
6. #if 0
7. /* currently setup by downloader */
8. .gpccon = 0xaa940659,
9. .gpccon_mask = 0xffffffff,
10. .gpcup = 0x0000ffff,
11. .gpcup_mask = 0xffffffff,
12. .gpdcon = 0xaa84aaa0,
13. .gpdcon_mask = 0xffffffff,
14. .gpdup = 0x0000faff,
15. .gpdup_mask = 0xffffffff,
16. #endif
17.
18. //.lpcsel = ((0xCE6) & ~7) | 1<<4,
19. };
以上为smdk2440_fb_info结构体定义。在此需要注释掉源码中的“.lpcsel = ((0xCE6) & ~7) | 1<<4”!本结构中的关键为“ .displays = &smdk2440_lcd_cfg,”,即LCD的硬件参数,需要根据具体的LCD屏幕来确定,小生使用的是天嵌3.5寸屏幕,所以该结构体的赋值如下:
4.5
1. static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
2.
3. .lcdcon5 = S3C2410_LCDCON5_FRM565 |
4. S3C2410_LCDCON5_INVVLINE |
5. S3C2410_LCDCON5_INVVFRAME |
6. S3C2410_LCDCON5_PWREN |
7. S3C2410_LCDCON5_HWSWP,
8.
9. .type = S3C2410_LCDCON1_TFT,
10.
11. .width = 320,
12. .height = 240,
13.
14. .pixclock = 80000, /* HCLK 100 MHz, divisor 3 */
15. .xres = 320,
16. .yres = 240,
17. .bpp = 16,
18.
19. .setclkval = 0x3,
20. .left_margin = 28, /* for HFPD*/
21. .right_margin = 24, /* for HBPD*/
22. .hsync_len = 42, /* for HSPW*/
23. .upper_margin = 6, /* for VFPD*/
24. .lower_margin = 2, /* for VBPD*/
25. .vsync_len = 12, /* for VSPW*/
26.
27. };
以上位LCD屏幕硬件参数。
注:
a.本文旨在将与友善之臂开发板配套的的linux-2.6.32.2内核移植到天嵌科技的TQ2440开发板上。
b. 硬件平台:天嵌科技TQ2440开发板(标配)
c. 软件平台:linux-2.6.32.2内核源码
第二部分:
基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.2(LCD驱动源码分析及移植之platform driver)
5.LCD驱动模块的注册与注销:
2. int __init s3c2410fb_init(void)
3. {
4. int ret = platform_driver_register(&s3c2410fb_driver);
5.
6. if (ret == 0)
7. ret = platform_driver_register(&s3c2412fb_driver);
8.
9. return ret;
10. }
11.
12. static void __exit s3c2410fb_cleanup(void)
13. {
14. platform_driver_unregister(&s3c2410fb_driver);
15. platform_driver_unregister(&s3c2412fb_driver);
16. }
17.
18. module_init(s3c2410fb_init);
19. module_exit(s3c2410fb_cleanup);
注册与注销模块由module_init宏与module_exit宏指定。
6.LCD平台设备驱动s3c2410fb_driver。
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. };
11.
12. static struct platform_driver s3c2412fb_driver = {
13. .probe = s3c2412fb_probe,
14. .remove = s3c2410fb_remove,
15. .suspend = s3c2410fb_suspend,
16. .resume = s3c2410fb_resume,
17. .driver = {
18. .name = "s3c2412-lcd",
19. .owner = THIS_MODULE,
20. },
21. };
该结构定义了LCD驱动程序的名字: "s3c2410-lcd",以及探测函数probe,移除函数remove,电源挂起函数suspend和电源恢复函数resume。
7.探测函数s3c2410fb_probe分析。
1. static int __init s3c2410fb_probe(struct platform_device *pdev)
2. {
3. return s3c24xxfb_probe(pdev, DRV_S3C2410);
4. }
5.
6. static int __init s3c2412fb_probe(struct platform_device *pdev)
7. {
8. return s3c24xxfb_probe(pdev, DRV_S3C2412);
9. }
由此可见,真正的探测函数为 s3c24xxfb_probe。
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. if (mach_info == NULL) {
17. dev_err(&pdev->dev,
18. "no platform data for lcd, cannot attach\n");
19. return -EINVAL;
20. }
21.
22. if (mach_info->default_display >= mach_info->num_displays) {
23. dev_err(&pdev->dev, "default is %d but only %d displays\n",
24. mach_info->default_display, mach_info->num_displays);
25. return -EINVAL;
26. }
27.
28. display = mach_info->displays + mach_info->default_display;
29.
30. irq = platform_get_irq(pdev, 0);
31. if (irq < 0) {
32. dev_err(&pdev->dev, "no irq for device\n");
33. return -ENOENT;
34. }
35.
36. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
37. if (!fbinfo)
38. return -ENOMEM;
39.
40. platform_set_drvdata(pdev, fbinfo);
41.
42. info = fbinfo->par;
43. info->dev = &pdev->dev;
44. info->drv_type = drv_type;
45.
46. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
47. if (res == NULL) {
48. dev_err(&pdev->dev, "failed to get memory registers\n");
49. ret = -ENXIO;
50. goto dealloc_fb;
51. }
52.
53. size = (res->end - res->start) + 1;
54. info->mem = request_mem_region(res->start, size, pdev->name);
55. if (info->mem == NULL) {
56. dev_err(&pdev->dev, "failed to get memory region\n");
57. ret = -ENOENT;
58. goto dealloc_fb;
59. }
60.
61. info->io = ioremap(res->start, size);
62. if (info->io == NULL) {
63. dev_err(&pdev->dev, "ioremap() of registers failed\n");
64. ret = -ENXIO;
65. goto release_mem;
66. }
67.
68. info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
69.
70. dprintk("devinit\n");
71.
72. strcpy(fbinfo->fix.id, driver_name);
73.
74. /* Stop the video */
75. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
76. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
77.
78. fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
79. fbinfo->fix.type_aux = 0;
80. fbinfo->fix.xpanstep = 0;
81. fbinfo->fix.ypanstep = 0;
82. fbinfo->fix.ywrapstep = 0;
83. fbinfo->fix.accel = FB_ACCEL_NONE;
84.
85. fbinfo->var.nonstd = 0;
86. fbinfo->var.activate = FB_ACTIVATE_NOW;
87. fbinfo->var.accel_flags = 0;
88. fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
89.
90. fbinfo->fbops = &s3c2410fb_ops;
91. fbinfo->flags = FBINFO_FLAG_DEFAULT;
92. fbinfo->pseudo_palette = &info->pseudo_pal;
93.
94. for (i = 0; i < 256; i++)
95. info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
96.
97. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
98. if (ret) {
99. dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
100. ret = -EBUSY;
101. goto release_regs;
102. }
103.
104. info->clk = clk_get(NULL, "lcd");
105. if (!info->clk || IS_ERR(info->clk)) {
106. printk(KERN_ERR "failed to get lcd clock source\n");
107. ret = -ENOENT;
108. goto release_irq;
109. }
110.
111. clk_enable(info->clk);
112. dprintk("got and enabled clock\n");
113.
114. msleep(1);
115.
116. info->clk_rate = clk_get_rate(info->clk);
117.
118. /* find maximum required memory size for display */
119. for (i = 0; i < mach_info->num_displays; i++) {
120. unsigned long smem_len = mach_info->displays[i].xres;
121.
122. smem_len *= mach_info->displays[i].yres;
123. smem_len *= mach_info->displays[i].bpp;
124. smem_len >>= 3;
125. if (fbinfo->fix.smem_len < smem_len)
126. fbinfo->fix.smem_len = smem_len;
127. }
128.
129. /* Initialize video memory */
130. ret = s3c2410fb_map_video_memory(fbinfo);
131. if (ret) {
132. printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
133. ret = -ENOMEM;
134. goto release_clock;
135. }
136.
137. dprintk("got video memory\n");
138.
139. fbinfo->var.xres = display->xres;
140. fbinfo->var.yres = display->yres;
141. fbinfo->var.bits_per_pixel = display->bpp;
142.
143. s3c2410fb_init_registers(fbinfo);
144.
145. s3c2410fb_check_var(&fbinfo->var, fbinfo);
146.
147. ret = s3c2410fb_cpufreq_register(info);
148. if (ret < 0) {
149. dev_err(&pdev->dev, "Failed to register cpufreq\n");
150. goto free_video_memory;
151. }
152.
153. ret = register_framebuffer(fbinfo);
154. if (ret < 0) {
155. printk(KERN_ERR "Failed to register framebuffer device: %d\n",
156. ret);
157. goto free_cpufreq;
158. }
159.
160. /* create device files */
161. ret = device_create_file(&pdev->dev, &dev_attr_debug);
162. if (ret) {
163. printk(KERN_ERR "failed to add debug attribute\n");
164. }
165.
166. printk(KERN_INFO "fb%d: %s frame buffer device\n",
167. fbinfo->node, fbinfo->fix.id);
168.
169. return 0;
170.
171. free_cpufreq:
172. s3c2410fb_cpufreq_deregister(info);
173. free_video_memory:
174. s3c2410fb_unmap_video_memory(fbinfo);
175. release_clock:
176. clk_disable(info->clk);
177. clk_put(info->clk);
178. release_irq:
179. free_irq(irq, info);
180. release_regs:
181. iounmap(info->io);
182. release_mem:
183. release_resource(info->mem);
184. kfree(info->mem);
185. dealloc_fb:
186. platform_set_drvdata(pdev, NULL);
187. framebuffer_release(fbinfo);
188. return ret;
189. }
7.1
1. mach_info = pdev->dev.platform_data;
2. if (mach_info == NULL) {
3. dev_err(&pdev->dev,
4. "no platform data for lcd, cannot attach\n");
5. return -EINVAL;
6. }
7.
8. if (mach_info->default_display >= mach_info->num_displays) {
9. dev_err(&pdev->dev, "default is %d but only %d displays\n",
10. mach_info->default_display, mach_info->num_displays);
11. return -EINVAL;
12. }
13.
14. display = mach_info->displays + mach_info->default_display;
第一行用于获取platform device中定义的平台数据(已经在本博客的上一篇文章中提及)。其中的mach_info定义如下:
1. struct s3c2410fb_mach_info *mach_info;
1. struct s3c2410fb_mach_info {
2.
3. struct s3c2410fb_display *displays; /* attached diplays info */
4. unsigned num_displays; /* number of defined displays */
5. unsigned default_display;
6.
7. /* GPIOs */
8.
9. unsigned long gpcup;
10. unsigned long gpcup_mask;
11. unsigned long gpccon;
12. unsigned long gpccon_mask;
13. unsigned long gpdup;
14. unsigned long gpdup_mask;
15. unsigned long gpdcon;
16. unsigned long gpdcon_mask;
17.
18. /* lpc3600 control register */
19. unsigned long lpcsel;
20. };
主要是记录LCD的一些属性,包括与显示参数相关的“ struct s3c2410fb_display *displays; ”及使用的GPIO引脚。而 ”if (mach_info->default_display >=mach_info->num_displays) {...}“用来查看显示屏幕的具体规格。
7.2
1. irq = platform_get_irq(pdev, 0);
2. if (irq < 0) {
3. dev_err(&pdev->dev, "no irq for device\n");
4. return -ENOENT;
5. }
获取设备中断号(定义于mach-smdk2440.c中)。
7.3
1. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
2. if (!fbinfo)
3. return -ENOMEM;
4.
5. platform_set_drvdata(pdev, fbinfo);
在内存中申请sizeof(struct s3c2410fb_info)+sizeof(struct fb_info)大小的空间,然后将获得的fbinfo设置为平台设备的驱动数据。其中framebuffer_alloc源码如下:
1. /**
2. * framebuffer_alloc - c
展开阅读全文