一、BSP修改及其分析
1. BSP中直接配置的四个寄存器
S3C6410数据手册的第14.5部分是显示控制器的编程模型部分,图1是开始部分截图。如第一行所说,这些寄存器是配置显示控制器用的。所以,所有的寄存器设置都围绕这些寄存器进行。
其中前面四个寄存器SPCON、MOFPCON、VIDCON0与VIDCON0,直接在BSP中进行配置。
图 1
A. SPCON和MOFPCON寄存器:如下代码中,配置SPCON和MOFPCON寄存器,使得显示控制器工作于:“RGB I/F类型”、“normal模式”。
- /* 修改内容1:LCD接口类型及模式设置
- * BSP中位于 static void __init jason6410_map_io(void){} 函数内。
- */
- /* set the LCD type */
- tmp = __raw_readl(S3C64XX_SPCON); //读取当前SPCON寄存器值
- tmp &= ~S3C64XX_SPCON_LCD_SEL_MASK; //清零LCD_SEL[1:0]
- tmp |= S3C64XX_SPCON_LCD_SEL_RGB; //置位最后一位,LCD_SEL[1:0] = 01
- __raw_writel(tmp, S3C64XX_SPCON); //写入LCD_SEL[1:0] = 01的值到SPCON寄存器,“RGB I/F style”
- /* remove the lcd bypass */
- tmp = __raw_readl(S3C64XX_MODEM_MIFPCON); //读取当前MOFPCON寄存器值
- tmp &= ~MIFPCON_LCD_BYPASS; //清零SEL_BYPASS[3]
- __raw_writel(tmp, S3C64XX_MODEM_MIFPCON); //写入SEL_BYPASS[3]已清零的值到MOFPCON寄存器,"normal mode"
- /* 使用到的宏定义 */
- #define S3C64XX_SPCON_LCD_SEL_MASK (0x3 << 0)
- #define S3C64XX_SPCON_LCD_SEL_RGB (0x1 << 0)
-
- #define MIFPCON_LCD_BYPASS (1 << 3)
B、VIDCON0和VIDCON1寄存器:如下代码中,配置VIDCON0和VIDCON1寄存器。
a.配置VIDCON0寄存器: 输出格式为“RGB I/F”,显示模式为“RGB Parallel format (RGB)”;
b.配置VIDCON1寄存器:配置行同步信号HSYNC与帧同步信号VSYNC极性取反。因为S3C6410的LCD RGB接口的时序中,HSYNC和VSYNC与一般TFT屏极性相反,故需要配置极性取反。如下图5为S3C6410的LCD RGB接口的时序,下图6为一般TFT屏工作时序。
- /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
- static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
- .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, //配置数据信号线GPIO
- .win[0] = &jason6410_fb_win0, //LCD屏参数
- .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, //配置VIDCON0寄存器
- .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, //配置VIDCON1寄存器
- };
-
- //配置LCD数据信号线GPIO
- extern void s3c64xx_fb_gpio_setup_24bpp(void)
- {
- s3c_gpio_cfgrange_nopull(S3C64XX_GPI(0), 16, S3C_GPIO_SFN(2)); //GPI
- s3c_gpio_cfgrange_nopull(S3C64XX_GPJ(0), 12, S3C_GPIO_SFN(2)); //GPJ
- }
- /* 使用到的宏定义 */
-
- #define VIDCON0_VIDOUT_RGB (0x0 << 26) //见下图2
- #define VIDCON0_PNRMODE_RGB (0x0 << 17) //见下图3
- #define VIDCON1_INV_HSYNC (1 << 6) //见下图4
- #define VIDCON1_INV_VSYNC (1 << 5) //见下图4
图 2
图 3
图4
图 5
图 6
2、帧缓冲设备参数计算及设置
上面讲到的四个寄存器,对于同一处理器下不同厂家、不同尺寸的TFT屏基本上都是一样的。但是,对于下面几个定时和硬件参数:行切换、帧切换、水平同步长度、垂直同步长度、X像素、Y像素。不同厂家的不同尺寸的TFT屏之间,往往是不同的,或者说是需要重新计算和修改的。
下面先解释上面图5中的几个参数:
A. VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin;
B. VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin;
C. VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len;
D. HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin;
E. HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin;
F. HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len;
这几个参数都是基于行数或者VCLK个数,所以其数据必须是严格的,不能有偏差,一旦有一个数不对,整个屏幕都不能显示。
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440, //pixel clock in ps (pico seconds)
- #endif
- .left_margin = 0x03, //time from sync to picture
- .right_margin = 0x02, //time from picture to sync
- .upper_margin = 0x01, //time from sync to picture
- .lower_margin = 0x01, //time from picture to sync
- .hsync_len = 0x28, //length of horizontal sync
- .vsync_len = 0x01, //length of vertical sync
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
-
- 附:下图摘自内核里的Documentation/fb/framebuffer.txt
- Video Mode Timings
- +----------+---------------------------------------------+----------+-------+
- | | ↑ | | |
- | | |upper_margin | | |
- | | ↓ | | |
- +----------###############################################----------+-------+
- | # ↑ # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | left # | # right | hsync |
- | margin # | xres # margin | len |
- |<-------->#<---------------+--------------------------->#<-------->|<----->|
- | # | # | |
- | # | # | |
- | # | # | |
- | # |yres # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # | # | |
- | # ↓ # | |
- +----------###############################################----------+-------+
- | | ↑ | | |
- | | |lower_margin | | |
- | | ↓ | | |
- +----------+---------------------------------------------+----------+-------+
- | | ↑ | | |
- | | |vsync_len | | |
- | | ↓ | | |
- +----------+---------------------------------------------+----------+-------+
3、BSP部分注册帧缓冲平台设备
如下代码完成s3c_device_fb平台设备结构体的设置。
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440,
- #endif
- .left_margin = 0x03,
- .right_margin = 0x02,
- .upper_margin = 0x01,
- .lower_margin = 0x01,
- .hsync_len = 0x28,
- .vsync_len = 0x01,
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
- /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
- static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
- .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
- .win[0] = &jason6410_fb_win0,
- .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
- .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
- };
-
- //static void __init jason6410_machine_init(void)函数中:
- s3c_fb_set_platdata(&jason6410_lcd_pdata);
参看arch/arm/plat-samsung/devs.c中FB部分,如下代码。- /* FB */
- #ifdef CONFIG_S3C_DEV_FB
- static struct resource s3c_fb_resource[] = {
- [0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K),
- [1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC),
- [2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO),
- [3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM),
- };
- struct platform_device s3c_device_fb = {
- .name = "s3c-fb",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_fb_resource),
- .resource = s3c_fb_resource,
- .dev = {
- .dma_mask = &samsung_device_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- };
- void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd)
- {
- s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
- &s3c_device_fb);
- }
- #endif /* CONFIG_S3C_DEV_FB */
最终,设置完s3c_device_fb平台设备结构体,结构体之间的关系如下:- //1. s3c_device_fb平台设备结构体
- struct platform_device s3c_device_fb = {
- .name = "s3c-fb",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_fb_resource),
- .resource = s3c_fb_resource,
- .dev = {
- .dma_mask = &samsung_device_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- };
-
- //2. s3c_device_fb平台设备结构体中的resource结构体
- static struct resource s3c_fb_resource[] = {
- [0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K),
- [1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC),
- [2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO),
- [3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM),
- };
-
- //3. s3c_device_fb平台设备结构体中的dev结构体里私有数据指针所指向的平台私有数据
- static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
- .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
- .win[0] = &jason6410_fb_win0,
- .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
- .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
- };
-
- //4. 平台私有数据jason6410_lcd_pdata结构体里的window_settings结构体
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440,
- #endif
- .left_margin = 0x03,
- .right_margin = 0x02,
- .upper_margin = 0x01,
- .lower_margin = 0x01,
- .hsync_len = 0x28,
- .vsync_len = 0x01,
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
-
- /*
- * 实际上,相当于有如下三个红色标注的结构体
- * 1. 在jason6410_machine_init函数里的platform_add_devices函数注册s3c_device_fb平台设备结构体;
- * 2. 通过私有数据指针找到jason6410_lcd_pdata结构体;
- * 3. 通过s3c_fb_pd_win结构体指针,找到
jason6410_fb_win0结构体;
- */
- struct platform_device s3c_device_fb = {
- .name = "s3c-fb",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_fb_resource),
- .resource = {
- [0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K),
- [1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC),
- [2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO),
- [3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM),
- }
- .dev = {
- .platform_data = &jason6410_lcd_pdata,
- .dma_mask = &samsung_device_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- };
-
- static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
- .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
- .win[0] = &jason6410_fb_win0,
- .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
- .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
- };
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440,
- #endif
- .left_margin = 0x03,
- .right_margin = 0x02,
- .upper_margin = 0x01,
- .lower_margin = 0x01,
- .hsync_len = 0x28,
- .vsync_len = 0x01,
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
- /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
- *
- * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
- * http://jason2012.blog.chinaunix.net
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/fb.h>
- #include <linux/gpio.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/dm9000.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
- #include <linux/serial_core.h>
- #include <linux/types.h>
- #include <asm/mach-types.h>
- #include <asm/mach/arch.h>
- #include <asm/mach/map.h>
- #include <mach/map.h>
- #include <mach/regs-gpio.h>
- #include <mach/regs-modem.h>
- #include <mach/regs-srom.h>
- #include <plat/s3c6410.h>
- #include <plat/adc.h>
- #include <plat/cpu.h>
- #include <plat/devs.h>
- #include <plat/fb.h>
- #include <plat/nand.h>
- #include <plat/regs-serial.h>
- #include <plat/ts.h>
- #include <plat/regs-fb-v4.h>
- #include <plat/iic.h>
- #include <video/platform_lcd.h>
- #define UCON S3C2410_UCON_DEFAULT
- #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
- #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
- static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
- [0] = {
- .hwport = 0,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [1] = {
- .hwport = 1,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [2] = {
- .hwport = 2,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [3] = {
- .hwport = 3,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- };
- /* Framebuffer. */
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440,
- #endif
- .left_margin = 0x03,
- .right_margin = 0x02,
- .upper_margin = 0x01,
- .lower_margin = 0x01,
- .hsync_len = 0x28,
- .vsync_len = 0x01,
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
- /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
- static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
- .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
- .win[0] = &jason6410_fb_win0,
- .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
- .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
- };
- /* Nand flash */
- static struct mtd_partition jason6410_nand_part[] = {
- {
- .name = "u-boot-2011.06",
- .offset = 0,
- .size = (4 * 128 *SZ_1K),
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "Linux Kernel 3.2.8",
- .offset = MTDPART_OFS_APPEND,
- .size = (5*SZ_1M) ,
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "UBI File System",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- }
- };
- static struct s3c2410_nand_set jason6410_nand_sets[] = {
- [0] = {
- .name = "nand",
- .nr_chips = 1,
- .nr_partitions = ARRAY_SIZE(jason6410_nand_part),
- .partitions = jason6410_nand_part,
- },
- };
- static struct s3c2410_platform_nand jason6410_nand_info = {
- .tacls = 25,
- .twrph0 = 55,
- .twrph1 = 40,
- .nr_sets = ARRAY_SIZE(jason6410_nand_sets),
- .sets = jason6410_nand_sets,
- };
- static struct platform_device *jason6410_devices[] __initdata = {
- &s3c_device_nand,
- &s3c_device_i2c0,
- &s3c_device_fb,
- };
- static struct i2c_board_info i2c_devs0[] __initdata = {
- { I2C_BOARD_INFO("24c08", 0x50), },
- };
- static struct i2c_board_info i2c_devs1[] __initdata = {
- /* Add your i2c device here */
- };
- static void __init jason6410_map_io(void)
- {
- u32 tmp;
- s3c64xx_init_io(NULL, 0);
- s3c24xx_init_clocks(12000000);
- s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));
- /* set the LCD type */
- tmp = __raw_readl(S3C64XX_SPCON);
- tmp &= ~S3C64XX_SPCON_LCD_SEL_MASK;
- tmp |= S3C64XX_SPCON_LCD_SEL_RGB;
- __raw_writel(tmp, S3C64XX_SPCON);
- /* remove the lcd bypass */
- tmp = __raw_readl(S3C64XX_MODEM_MIFPCON);
- tmp &= ~MIFPCON_LCD_BYPASS;
- __raw_writel(tmp, S3C64XX_MODEM_MIFPCON);
- }
- static void __init jason6410_machine_init(void)
- {
- s3c_device_nand.name = "s3c6410-nand";
- s3c_nand_set_platdata(&jason6410_nand_info);
- s3c_i2c0_set_platdata(NULL);
- s3c_fb_set_platdata(&jason6410_lcd_pdata);
- if (ARRAY_SIZE(i2c_devs0)) {
- i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
- }
- if (ARRAY_SIZE(i2c_devs1)) {
- i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
- }
- platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
- }
- MACHINE_START(JASON6410, "JASON6410")
- /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
- .atag_offset = 0x100,
- .init_irq = s3c6410_init_irq,
- .map_io = jason6410_map_io,
- .init_machine = jason6410_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
4、帧缓冲平台驱动
4.1 s3c_fb_driver平台驱动结构体及其注册
平台设备与平台驱动匹配的函数如下,先尝试OF类型的匹配,如果匹配不成功,尝试驱动的id_table里是否有与设备名匹配的id,如果还是不能匹配,就回到基于驱动名和设备名的匹配。匹配成功返回1,失败返回0。
在这里,设备驱动结构体未定义of_match_table,故第一次匹配是不成功的。然后,平台驱动结构体里定义了id_table,且有与平台设备名相同的平台设备ID “s3c-fb”,因此在此匹配,并且把平台设备结构体的id_entry赋值为匹配的ID项。
- //平台设备设备与驱动匹配函数
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- /* Attempt an OF style match first */
- if (of_driver_match_device(dev, drv))
- return 1;
- /* Then try to match against the id table */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0);
- }
-
- //platform_match_id函数
- static const struct platform_device_id *platform_match_id(
- const struct platform_device_id *id,
- struct platform_device *pdev)
- {
- while (id->name[0]) {
- if (strcmp(pdev->name, id->name) == 0) {
- pdev->id_entry = id;
- return id;
- }
- id++;
- }
- return NULL;
- }
- //@driver/video/s3c-fb.c
-
- //s3c_fb_driver平台驱动结构体
- static struct platform_driver s3c_fb_driver = {
- .probe = s3c_fb_probe,
- .remove = __devexit_p(s3c_fb_remove),
- .id_table = s3c_fb_driver_ids,
- .driver = {
- .name = "s3c-fb",
- .owner = THIS_MODULE,
- .pm = &s3cfb_pm_ops,
- },
- };
-
- //s3c_fb_driver平台驱动结构体注册
- static int __init s3c_fb_init(void)
- {
- return platform_driver_register(&s3c_fb_driver);
- }
-
- //s3c_fb_driver平台驱动结构体id_table
- static struct platform_device_id s3c_fb_driver_ids[] = {
- {
- .name = "s3c-fb", //匹配平台设备名的名字
- .driver_data = (unsigned long)&s3c_fb_data_64xx,
- }, {
- .name = "s5pc100-fb",
- .driver_data = (unsigned long)&s3c_fb_data_s5pc100,
- }, {
- .name = "s5pv210-fb",
- .driver_data = (unsigned long)&s3c_fb_data_s5pv210,
- }, {
- .name = "exynos4-fb",
- .driver_data = (unsigned long)&s3c_fb_data_exynos4,
- }, {
- .name = "s3c2443-fb",
- .driver_data = (unsigned long)&s3c_fb_data_s3c2443,
- }, {
- .name = "s5p64x0-fb",
- .driver_data = (unsigned long)&s3c_fb_data_s5p64x0,
- },
- {},
- };
-
- //平台设备ID结构体中的驱动数据
- static struct s3c_fb_driverdata s3c_fb_data_64xx = {
- .variant = {
- .nr_windows = 5,
- .vidtcon = VIDTCON0,
- .wincon = WINCON(0),
- .winmap = WINxMAP(0),
- .keycon = WKEYCON,
- .osd = VIDOSD_BASE,
- .osd_stride = 16,
- .buf_start = VIDW_BUF_START(0),
- .buf_size = VIDW_BUF_SIZE(0),
- .buf_end = VIDW_BUF_END(0),
- .palette = {
- [0] = 0x400,
- [1] = 0x800,
- [2] = 0x300,
- [3] = 0x320,
- [4] = 0x340,
- },
- .has_prtcon = 1,
- .has_clksel = 1,
- },
- .win[0] = &s3c_fb_data_64xx_wins[0],
- .win[1] = &s3c_fb_data_64xx_wins[1],
- .win[2] = &s3c_fb_data_64xx_wins[2],
- .win[3] = &s3c_fb_data_64xx_wins[3],
- .win[4] = &s3c_fb_data_64xx_wins[4],
- };
4.2 s3c_fb_probe函数分析
- static int __devinit s3c_fb_probe(struct platform_device *pdev)
- {
- const struct platform_device_id *platid;
- struct s3c_fb_driverdata *fbdrv;
- struct device *dev = &pdev->dev;
- struct s3c_fb_platdata *pd;
- struct s3c_fb *sfb;
- struct resource *res;
- int win;
- int ret = 0;
-
- //platid指向平台设备结构体里的id_entry成员,即
platid = pdev->id_entry;
- platid = platform_get_device_id(pdev);
- //fbdrv指向平台设备结构体id_entry项的driver_data成员
- fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;
-
- //判断是否超出最大硬件支持的最大窗口数
- if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
- dev_err(dev, "too many windows, cannot attach\n");
- return -EINVAL;
- }
-
- //指向平台设备的平台数据
- pd = pdev->dev.platform_data;
- if (!pd) {
- dev_err(dev, "no platform data specified\n");
- return -EINVAL;
- }
-
- //1. 给s3c_fb结构体分配内存
- sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
- if (!sfb) {
- dev_err(dev, "no memory for framebuffers\n");
- return -ENOMEM;
- }
- dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
-
- //2. 设置s3c_fb结构体
- sfb->dev = dev; //指向设备
- sfb->pdata = pd; //指向平台设备的平台数据
- sfb->variant = fbdrv->variant; //s3c_fb_variant结构体拷贝(结构体完全一样可直接拷贝)
- spin_lock_init(&sfb->slock); //初始化自旋锁
-
- //3. 获取并使能模块总时钟
- sfb->bus_clk = clk_get(dev, "lcd"); //找到对应的bus clock,模块总时钟
- if (IS_ERR(sfb->bus_clk)) {
- dev_err(dev, "failed to get bus clock\n");
- ret = PTR_ERR(sfb->bus_clk);
- goto err_sfb;
- }
- clk_enable(sfb->bus_clk); //使能模块总时钟
- if (!sfb->variant.has_clksel) { //判断driver_data里是否已定义源时钟
- sfb->lcd_clk = clk_get(dev, "sclk_fimd");//未定义,用“sclk_fimd”去clock文件里找
- if (IS_ERR(sfb->lcd_clk)) {
- dev_err(dev, "failed to get lcd clock\n");
- ret = PTR_ERR(sfb->lcd_clk);
- goto err_bus_clk;
- }
- clk_enable(sfb->lcd_clk); //使能clock文件里找到的时钟源
- }
- pm_runtime_enable(sfb->dev); //使能设备运行时电源管理
-
- //4. 获取平台设备数据
- //获取资源起始地址、结束地址,是显示控制器寄存器组地址
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "failed to find registers\n");
- ret = -ENOENT;
- goto err_lcd_clk;
- }
-
- //为显示控制器寄存器组分配内存
- sfb->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!sfb->regs_res) {
- dev_err(dev, "failed to claim register region\n");
- ret = -ENOENT;
- goto err_lcd_clk;
- }
-
- //把寄存器组地址映射到内存
- //注:这里使用ioremap,所以,BSP中不需要编写针对LCD的I/O内存静态映射;
- // 即static struct map_desc jason6410_iodesc[]{};里面这部分。
- sfb->regs = ioremap(res->start, resource_size(res));
- if (!sfb->regs) {
- dev_err(dev, "failed to map registers\n");
- ret = -ENXIO;
- goto err_req_region;
- }
-
- //获取资源中的中断部分
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "failed to acquire irq resource\n");
- ret = -ENOENT;
- goto err_ioremap;
- }
- sfb->irq_no = res->start;
- ret = request_irq(sfb->irq_no, s3c_fb_irq, //注册中断处理函数
- 0, "s3c_fb", sfb);
- if (ret) {
- dev_err(dev, "irq request failed\n");
- goto err_ioremap;
- }
- dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
-
- //pdev->dev->p->driver_data = sfb
- platform_set_drvdata(pdev, sfb);
- //运行时恢复操作的入口
- pm_runtime_get_sync(sfb->dev);
- /* setup gpio and output polarity controls */
- //5. 设置寄存器组
- pd->setup_gpio(); //设置数据线的GPIO,pd见23行,即s3c64xx_fb_gpio_setup_24bpp,见BSP
- writel(pd->vidcon1, sfb->regs + VIDCON1); //写VIDCON1寄存器
- /* zero all windows before we do anything */
- for (win = 0; win < fbdrv->variant.nr_windows; win++) //复位所有窗口寄存器
- s3c_fb_clear_win(sfb, win);
- /* initialise colour key controls */ //初始化
- for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
- void __iomem *regs = sfb->regs + sfb->variant.keycon;
- regs += (win * 8); //GNU下空指针可以算数运算,并且算法操作与char *一致
- writel(0xffffff, regs + WKEYCON0); //详见S3C6410X.pdf 516页
- writel(0xffffff, regs + WKEYCON1);
- }
- /* we have the register setup, start allocating framebuffers */
- //6. 设置完寄存器,开始分配帧缓冲
- for (win = 0; win < fbdrv->variant.nr_windows; win++) {
- if (!pd->win[win])
- continue;
- if (!pd->win[win]->win_mode.pixclock) //如果未定义像素时钟,在此计算
- s3c_fb_missing_pixclock(&pd->win[win]->win_mode); //计算像素时钟
-
- //注册硬件窗口,下面展开细说
- ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
- &sfb->windows[win]);
- if (ret < 0) {
- dev_err(dev, "failed to create window %d\n", win);
- for (; win >= 0; win--)
- s3c_fb_release_win(sfb, sfb->windows[win]);
- goto err_irq;
- }
- }
-
- //再一次,同line 112/114
- platform_set_drvdata(pdev, sfb);
- pm_runtime_put_sync(sfb->dev);
- return 0;
- err_irq:
- free_irq(sfb->irq_no, sfb);
- err_ioremap:
- iounmap(sfb->regs);
- err_req_region:
- release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
- err_lcd_clk:
- if (!sfb->variant.has_clksel) {
- clk_disable(sfb->lcd_clk);
- clk_put(sfb->lcd_clk);
- }
- err_bus_clk:
- clk_disable(sfb->bus_clk);
- clk_put(sfb->bus_clk);
- err_sfb:
- kfree(sfb);
- return ret;
- }
4.3 s3c_fb_probe_win函数分析(注册硬件窗口)- /**
- * s3c_fb_probe_win() - register an hardware window
- * @sfb: The base resources for the hardware
- * @variant: The variant information for this window.
- * @res: Pointer to where to place the resultant window.
- *
- * Allocate and do the basic initialisation for one of the hardware's graphics
- * windows.
- */
- static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
- struct s3c_fb_win_variant *variant,
- struct s3c_fb_win **res)
- {
- struct fb_var_screeninfo *var;
- struct fb_videomode *initmode;
- struct s3c_fb_pd_win *windata;
- struct s3c_fb_win *win;
- struct fb_info *fbinfo;
- int palette_size;
- int ret;
- dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
- init_waitqueue_head(&sfb->vsync_info.wait); //初始化等待队列头
- palette_size = variant->palette_sz * 4; //设置调色板大小
-
- //1.创建fb_info结构体,并分配驱动私有数据
- fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
- palette_size * sizeof(u32), sfb->dev);
- if (!fbinfo) {
- dev_err(sfb->dev, "failed to allocate framebuffer\n");
- return -ENOENT;
- }
-
- //windata指向平台设备驱动私有数据里的win[win_no]成员,这里是BSP中的jason6410_fb_win0结构体
- //initmode指向jason6410_fb_win结构体里的win_mode成员
- static struct s3c_fb_pd_win jason6410_fb_win0 = {
- /* this is to ensure we use win0 */
- .win_mode = {
- #if 0
- .pixclock = 115440,
- #endif
- .left_margin = 0x03,
- .right_margin = 0x02,
- .upper_margin = 0x01,
- .lower_margin = 0x01,
- .hsync_len = 0x28,
- .vsync_len = 0x01,
- .xres = 480,
- .yres = 272,
- },
- .max_bpp = 32,
- .default_bpp = 16,
- };
- windata = sfb->pdata->win[win_no];
- initmode = &windata->win_mode;
- WARN_ON(windata->max_bpp == 0); //调用dump_stack,打印堆栈信息
- WARN_ON(windata->win_mode.xres == 0);
- WARN_ON(windata->win_mode.yres == 0);
-
- //2. 帧缓冲每个窗口的s3c_fb_win结构体设置
- win = fbinfo->par; //见代码30行分配fb_info结构体部分,在那fbinfo->par已指向驱动私有数据
- *res = win; //*res指向win
- var = &fbinfo->var; //var指向fb_var_screeninfo结构体
- win->variant = *variant; //结构体复制(同一类型结构体可以复制)
- win->fbinfo = fbinfo; //fbinfo指向fb_info结构体
- win->parent = sfb; //parent指向s3c_fb结构体
- win->windata = windata; //windata指向s3c_fb_pd_win结构体
- win->index = win_no; //编号
- win->palette_buffer = (u32 *)(win + 1); //指向调色板缓冲区
-
- //3. 为帧缓冲的窗口分配显示内存
- ret = s3c_fb_alloc_memory(sfb, win);
- if (ret) {
- dev_err(sfb->dev, "failed to allocate display memory\n");
- return ret;
- }
- /* setup the r/b/g positions for the window's palette */
- //4. 为窗口调色板设置r/g/b的位置,这里的palette_16bpp为初始化值1,程序中未对其赋值
- //该结构体位于4.1节
- struct s3c_fb_win_variant {
- unsigned int has_osd_c:1;
- unsigned int has_osd_d:1;
- unsigned int has_osd_alpha:1;
- unsigned int palette_16bpp:1;
- unsigned short osd_size_off;
- unsigned short palette_sz;
- u32 valid_bpp;
- };
- if (win->variant.palette_16bpp) {
- /* Set RGB 5:6:5 as default */
- win->palette.r.offset = 11;
- win->palette.r.length = 5;
- win->palette.g.offset = 5;
- win->palette.g.length = 6;
- win->palette.b.offset = 0;
- win->palette.b.length = 5;
- } else {
- /* Set 8bpp or 8bpp and 1bit alpha */
- win->palette.r.offset = 16;
- win->palette.r.length = 8;
- win->palette.g.offset = 8;
- win->palette.g.length = 8;
- win->palette.b.offset = 0;
- win->palette.b.length = 8;
- }
- /* setup the initial video mode from the window */
- //5. 设置初始视频模式,填充结构体fbinfo
- //将fb_videomode结构体中成员赋值给fb_var_screeninfo结构体对应成员
- fb_videomode_to_var(&fbinfo->var, initmode);
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
- fbinfo->var.bits_per_pixel = windata->default_bpp;
- fbinfo->fbops = &s3c_fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &win->pseudo_palette;
- /* prepare to actually start the framebuffer */
- //6. 检查可变参数,帧缓冲层核实信息,并根据硬件能力更新几种信息。
- ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
- if (ret < 0) {
- dev_err(sfb->dev, "check_var failed on initial video params\n");
- return ret;
- }
- /* create initial colour map */
- //7. 分配和设置fb_cmap结构体,该结构体记录设备无关的颜色表信息
- ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
- if (ret == 0)
- fb_set_cmap(&fbinfo->cmap, fbinfo);
- else
- dev_err(sfb->dev, "failed to allocate fb cmap\n");
-
- //8. 设置帧缓冲模式
- s3c_fb_set_par(fbinfo);
- dev_dbg(sfb->dev, "about to register framebuffer\n");
- /* run the check_var and set_par on our configuration. */
-
- //9. 注册一个帧缓冲设备
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- dev_err(sfb->dev, "failed to register framebuffer\n");
- return ret;
- }
- dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
- return 0;
- }
5、帧缓冲设备的用户空间访问
运行如下代码,在LCD显示屏上绘制R/G/B这三种颜色的由浅入深的变化情况。
- /*
- * S3C6410 framebuffer test programs
- */
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <linux/fb.h>
- #include <sys/mman.h>
- int main()
- {
- int fbfd = 0;
- struct fb_var_screeninfo vinfo;
- unsigned long screensize = 0;
- char *fbp = 0;
- int x = 0, y = 0;
- int i = 0;
- // Open the file for reading and writing
- fbfd = open("/dev/fb0", O_RDWR);
- if (!fbfd) {
- printf("Error: cannot open framebuffer device.\n");
- exit(1);
- }
- printf("The framebuffer device was opened successfully.\n");
- // Get variable screen information
- if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
- printf("Error reading variable information.\n");
- exit(1);
- }
- printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
- if (vinfo.bits_per_pixel != 16) {
- printf("Error: not supported bits_per_pixel, it only supports 16 bit color\n");
- exit(1);
- }
- // Figure out the size of the screen in bytes
- screensize = vinfo.xres * vinfo.yres * 2;
- // Map the device to memory
- fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
- fbfd, 0);
- if ((int)fbp == -1) {
- printf("Error: failed to map framebuffer device to memory.\n");
- exit(4);
- }
- printf("The framebuffer device was mapped to memory successfully.\n");
- // Draw 3 rect with graduated RED/GREEN/BLUE
- for (i = 0; i < 3; i++) {
- for (y = i * (vinfo.yres / 3); y < (i + 1) * (vinfo.yres / 3); y++) {
- for (x = 0; x < vinfo.xres; x++) {
- long location = x * 2 + y * vinfo.xres * 2;
- int r = 0, g = 0, b = 0;
- unsigned short rgb;
- if (i == 0)
- r = ((x * 1.0) / vinfo.xres) * 32;
- if (i == 1)
- g = ((x * 1.0) / vinfo.xres) * 64;
- if (i == 2)
- b = ((x * 1.0) / vinfo.xres) * 32;
- rgb = (r << 11) | (g << 5) | b;
- *((unsigned short*)(fbp + location)) = rgb;
- }
- }
- }
- munmap(fbp, screensize);
- close(fbfd);
- return 0;
- }
测试方法(LCD尺寸有所不同,仅作参考):
二、内核修改
下列代码是I/O静态内存映射,s3c-fb.c里通过ioremap映射需配置的寄存器,所以无需做静态内存映射,所以内核部分可不做任何修改(不包括BSP)。如果驱动里用的是虚拟地址,则必须使用下列代码。
- gedit arch/arm/plat-samsung/include/plat/map-base.h
- line 39 add:
- #define S3C_VA_LCD S3C_ADDR(0x01100000) /* LCD */
在BSP中添加:- static struct map_desc jason6410_iodesc[] = {
- {
- /* LCD support */
- .virtual = (unsigned long)S3C_VA_LCD, //虚拟地址
- .pfn = __phys_to_pfn(S3C_PA_FB), //物理地址
- .length = SZ_16K,
- .type = MT_DEVICE,
- },
- };
-
- static void __init jason6410_map_io(void)
- {
- s3c64xx_init_io(jason6410_iodesc, ARRAY_SIZE(jason6410_iodesc)); //初始化
- ......
- }
三、内核配置
- Device Drivers --->
- Graphics support --->
- <*> Support for frame buffer devices --->
- <*> Samsung S3C framebuffer support
- Device Drivers --->
- Graphics support --->
- [ ] Backlight & LCD device support --->
- Device Drivers --->
- Graphics support --->
- Console display driver support --->
- <*> Framebuffer Console support
- Device Drivers --->
- Graphics support --->
- [*] Bootup logo --->
四、启动Logo修改
1. 找到自己喜欢的图片,用Photoshop裁剪为屏幕尺寸大小,这里是480*272;
2. PS软件里选 文件 ---> 存储为,在弹出框里输入保存名,保存类型选BMP格式;按确定后会弹出如下窗口,如红色方框内选择,选择深度为24位。产生logo.bmp(480*272,24bpp),在此 logo.zip 3. 重复步骤2中前面步骤,在最后的弹出框点选高级模式,并选如下图所示,产生logo_cat.bmp(480*272,16bpp,R/G/B:5/6/5),该图用于"cat logo_cat.bmp > /dev/fb0"演示。 logo_cat.zip
4. 接下来的步骤在Linux下进行,将步骤2生成的logo.bmp并不能用在内核中,需做进一步修改,先把它拷贝到虚拟机里某一目录下,执行如下转换。
- # bmptoppm logo.bmp > logo.ppm
- # ppmquant 224 logo.ppm > logo_224.ppm
- # pnmnoraw logo_224.ppm > logo_linux_clut224.ppm
5. 将上一步骤中生成的logo_linux_clut224.ppm拷贝到内核目录:drivers/video/logo/覆盖原有图片。 6. 如果已经编译过内核,直接再次编译,图片很有可能是原来的或者是没显示,可以执行如下命令清楚中间结果,而不必用make distclean清空,实在不行再用此命令,有时会有的,不知为何,呵呵。
- # rm drivers/video/logo/.l*
7. 执行如下命令,烧写内核,就能看到启动界面。
8. 内核挂上文件系统后,传入步骤3中产生的logo_cat.bmp,执行如下命令,能看到图片。现在还有点问题,图片倒过来的,并且水平有偏移。
- cat logo_cat.bmp > /dev/fb0
注意:在内核里必须勾选如下选项,不选择,是显示不了启动图片的。- Device Drivers --->
- Graphics support --->
- Console display driver support --->
- <*> Framebuffer Console support
附录1:启动界面
附录2:cat logo_cat.bmp > /dev/fb0 后屏幕显示
五、启动QT
现在做的主要是内核移植,文件系统用的还是友善自带的,但是如上修改后,界面程序还是启动不了,且无人任何错误信息。文件系统里是有保证界面启动的库函数的,所以问题应该出现在驱动或者内核配置上。
友善自带的文件系统里有三个界面程序,我试着打开qt4(“/bin/qt4 &” 命令)那个界面时,有错误提示了,先是socket相关错误,就照着友善2.6.38内核的配置,配置了Networking support下的内核选项。接着报信号量错误,就照着友善的配置在Gernel Setup下配置内核选项。现在能启动界面了。
还有一个问题是,友善文件系统里自带的rcS有输出信息输出到显示屏上,不好看。就在文件系统里的etc/init.d/rcS里,把所有 "> /dev/tty1"都注释掉,这样这些打印信息只会在串口终端里显示,不会影响界面显示了。用如下命令生成新的ubi文件系统。
- # mkubimage-slc rootfs_qtopia_qt4 rootfs_qtopia_qt4-slc.ubi
阅读(947) | 评论(0) | 转发(0) |