1、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[] =
2、{ 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
3、 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
4、 .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资源。
5、 其中, 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
6、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_
7、START(S3C2440, "SMDK2440")
2. /* Maintainer: Ben Dooks
8、 = 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. s3
9、c24xx_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添加进平台设备的私有结
10、构中。 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;
11、 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
12、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 =
13、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
14、 = &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.
15、 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
16、 = 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,
17、 /* 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.
18、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
19、 { 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_dr
20、iver 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
21、 }, 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
22、 "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,
23、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 dr
24、v_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.
25、 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.
26、 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.
27、 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
28、); 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;
29、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.
30、 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 + ((dr
31、v_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->i
32、o + 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.
33、 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
34、>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
35、 %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.
36、 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_
37、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.
38、 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 relea
39、se_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_va
40、r(&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) {
41、 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_
42、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. s3
43、c2410fb_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->
44、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
45、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.
46、 } 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
47、 */ 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. unsigne
48、d 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 (mac
49、h_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_al
50、loc(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






