LCD(四)平台设备驱动之 platform_device

一 、重要结构体

1、fb_info结构体

struct fb_info {
    int node;
    int flags;
    struct mutex lock;            /* 用于open/release/ioctl的锁*/
    struct fb_var_screeninfo var;/*LCD可变参数*/
    struct fb_fix_screeninfo fix;/*LCD固定参数*/
    struct fb_monspecs monspecs; /*LCD显示器标准*/
    struct work_struct queue;    /*帧缓冲事件队列*/
    struct fb_pixmap pixmap;     /*图像硬件mapper*/
    struct fb_pixmap sprite;     /*光标硬件mapper*/
    struct fb_cmap cmap;         /*当前的颜色表*/
    struct fb_videomode *mode;   /*当前的显示模式*/
 
#ifdef CONFIG_FB_BACKLIGHT
    struct backlight_device *bl_dev;/*对应的背光设备*/
    struct mutex bl_curve_mutex;
    u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    struct delayed_work deferred_work;
    struct fb_deferred_io *fbdefio;
#endif
 
    struct fb_ops *fbops; /*对底层硬件操作的函数指针*/
    struct device *device;
    struct device *dev;   /*fb设备*/
    int class_flag;    
#ifdef CONFIG_FB_TILEBLITTING
    struct fb_tile_ops *tileops; /*图块Blitting*/
#endif
    char __iomem *screen_base;   /*虚拟基地址*/
    unsigned long screen_size;   /*LCD IO映射的虚拟内存大小*/
    void *pseudo_palette;        /*伪16色颜色表*/
#define FBINFO_STATE_RUNNING    0
#define FBINFO_STATE_SUSPENDED  1
    u32 state;  /*LCD的挂起或恢复状态*/
    void *fbcon_par;
    void *par;    
};

其中,比较重要的成员有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和struct fb_ops *fbops,他们也都是结构体

2、struct fb_var_screeninfo

//fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数

struct fb_var_screeninfo {
    __u32 xres;                /*可见屏幕一行有多少个像素点*/
    __u32 yres;                /*可见屏幕一列有多少个像素点*/
    __u32 xres_virtual;        /*虚拟屏幕一行有多少个像素点*/       
    __u32 yres_virtual;        /*虚拟屏幕一列有多少个像素点*/
    __u32 xoffset;             /*虚拟到可见屏幕之间的行偏移*/
    __u32 yoffset;             /*虚拟到可见屏幕之间的列偏移*/
    __u32 bits_per_pixel;      /*每个像素的位数即BPP*/
    __u32 grayscale;           /*非0时,指的是灰度*/
 
    struct fb_bitfield red;    /*fb缓存的R位域*/
    struct fb_bitfield green;  /*fb缓存的G位域*/
    struct fb_bitfield blue;   /*fb缓存的B位域*/
    struct fb_bitfield transp; /*透明度*/   
 
    __u32 nonstd;              /* != 0 非标准像素格式*/
    __u32 activate;                
    __u32 height;              /*高度*/
    __u32 width;               /*宽度*/
    __u32 accel_flags;    
 
    /*定时:除了pixclock本身外,其他的都以像素时钟为单位*/
    __u32 pixclock;            /*像素时钟(皮秒)*/
    __u32 left_margin;         /*行切换,从同步到绘图之间的延迟*/
    __u32 right_margin;        /*行切换,从绘图到同步之间的延迟*/
    __u32 upper_margin;        /*帧切换,从同步到绘图之间的延迟*/
    __u32 lower_margin;        /*帧切换,从绘图到同步之间的延迟*/
    __u32 hsync_len;           /*水平同步的长度*/
    __u32 vsync_len;           /*垂直同步的长度*/
    __u32 sync;
    __u32 vmode;
    __u32 rotate;
    __u32 reserved[5];         /*保留*/
};
3、fb_fix_screeninfo结构体
//fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数

struct fb_fix_screeninfo {
    char id[16];                /*字符串形式的标示符 */
    unsigned long smem_start;   /*fb缓存的开始位置 */
    __u32 smem_len;             /*fb缓存的长度 */
    __u32 type;                 /*看FB_TYPE_* */
    __u32 type_aux;             /*分界*/
    __u32 visual;               /*看FB_VISUAL_* */
    __u16 xpanstep;             /*如果没有硬件panning就赋值为0 */
    __u16 ypanstep;             /*如果没有硬件panning就赋值为0 */
    __u16 ywrapstep;            /*如果没有硬件ywrap就赋值为0 */
    __u32 line_length;          /*一行的字节数 */
    unsigned long mmio_start;   /*内存映射IO的开始位置*/
    __u32 mmio_len;             /*内存映射IO的长度*/
    __u32 accel;
    __u16 reserved[3];          /*保留*/
};
4、fb_ops结构体
//fb_ops结构体是对底层硬件操作的函数指针,该结构体中定义了对硬件的操作有:(这里只列出了常用的操作)

struct fb_ops {
    struct module *owner;
    //检查可变参数并进行设置
    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
 
    //根据设置的值进行更新,使之有效
    int (*fb_set_par)(struct fb_info *info);
 
    //设置颜色寄存器
    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
             unsigned blue, unsigned transp, struct fb_info *info);
 
    //显示空白
    int (*fb_blank)(int blank, struct fb_info *info);
 
    //矩形填充
    void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
 
    //复制数据
    void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
 
    //图形填充
    void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
};

二、帧缓冲设备作为平台设备

S5PV210的LCD控制器被集成在芯片内部作为一个相对独立的单元,所以Linux把它看做一个平台设备,故在内核代码/arch/arm/plat-s5p/devs.c中需要定义LCD相关的平台设备及资源,代码如下:

/* LCD Controller */
//LCD控制器的资源信息
static struct resource s5p_lcd_resource[] = {
    [0] = {
        .start = S5P_PA_LCD, //控制器IO端口开始地址
        .end = S5P_PA_LCD+ S5P_SZ_LCD- 1,//控制器IO端口结束地址
        .flags = IORESOURCE_MEM,//标识为LCD控制器IO端口,在驱动中引用这个就表示引用IO端口
    },
    [1] = {
        .start = IRQ_LCD1,//LCD中断
        .end = IRQ_LCD1,
        .flags = IORESOURCE_IRQ,//标识为LCD中断
    },
    [2] = {
        .start = IRQ_LCD0,
        .end = IRQ_LCD0,
        .flags = IORESOURCE_IRQ,
    },
};
 
static u64 s5p_device_lcd_dmamask= 0xffffffffUL;
 
struct platform_device s5p_device_lcd = {
    .name         = "s5pv210-lcd",//作为平台设备的LCD设备名
    .id         = -1,
    .num_resources = ARRAY_SIZE(s5p_lcd_resource),//资源数量
    .resource     = s5p_lcd_resource,//引用上面定义的资源
    .dev = {
        .dma_mask = &s5p_device_lcd_dmamask,  //设备DMA可以访问的内存范围(0xffffffffUL)
        .coherent_dma_mask = 0xffffffffUL     //作用于申请一致性的DMA缓冲区
    }
};
 
EXPORT_SYMBOL(s5p_device_lcd);//导出定义的LCD平台设备,好在mach-tq210.c的tq210_devices[]中添加到平台设备列表中

/arch/arm/plat-samsung/dev-fb.c中添加如下,s3c_fb_set_platform用于挂接fb平台设备的私有数据

void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd)
{
	struct s3c_platform_fb *npd;

	if (!pd) {
		printk(KERN_ERR "%s: no platform data\n", __func__);
		return;
	}
    //这里就是将内核中定义的s3c_platform_fb 结构体数据保存到LCD平台数据中,所以在写驱动的时候
    //就可以直接在平台数据中获取s3c_platform_fb 结构体的数据(即LCD各种参数信息)进行操作
	npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);

        if (!npd)
		printk(KERN_ERR "%s: no memory for platform data\n", __func__);
        else {
                for (i = 0; i < npd->nr_wins; i++)
                npd->nr_buffers[i] = 1;

                npd->nr_buffers[npd->default_win] = CONFIG_FB_S3C_NR_BUFFERS; // 再进一步对数据结构进行填充

                s3cfb_get_clk_name(npd->clk_name);
                npd->clk_on = s3cfb_clk_on;
                npd->clk_off = s3cfb_clk_off;

                /* starting physical address of memory region */
                npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1);
                /* size of memory region */
                npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMD, 1);

                s3c_device_fb.dev.platform_data = npd;     // 把传进来的 s3c_platform_fb 结构体变量挂载到  s3c_device_fb变量中
        }

	s5pv210_device_lcd.dev.platform_data = npd;
}

arch/arm/mach-sp5v210/include/mach/map.h中添加相关宏定义

#define S5PV210_PA_LCD          (0xF8000000)
#define S5P_PA_LCD              S5PV210_PA_LCD
#define S5PV210_SZ_LCD          SZ_1M 
#define S5P_SZ_LCD              S5PV210_SZ_LCD

意外之喜:如果前期配置终端显示LOGO没有成功,配置完以上代码,发现启动后显示有企鹅LOGO。

除此之外需要在/arch/arm/plat-samsung/include/plat/fb.h中为LCD平台设备定义了一个 s3c_platform_fb结构体,该结构体主要是记录LCD的硬件参数信息(比如该结构体的s3cfb_lcd成员结构中就用于记录LCD的屏幕尺寸、屏幕信息、可变的屏幕参数、LCD配置寄存器等),这样在写驱动的时候就直接使用这个结构体。

#define FB_SWAP_WORD    (1 << 24)
#define FB_SWAP_HWORD    (1 << 16)
#define FB_SWAP_BYTE    (1 << 8)
#define FB_SWAP_BIT    (1 << 0)

struct platform_device;
struct clk;

struct s3c_platform_fb {
	int		hw_ver;
	char		clk_name[16];
	int		nr_wins; //虚拟窗口的数量
	int		nr_buffers[5];
	int		default_win; //当前默认的窗口
	int		swap;
	phys_addr_t	pmem_start[5]; /* starting physical address of memory region */ /显存的物理起始地址
	size_t		pmem_size[5]; /* size of memory region */ //显存的大小
	void            *lcd;  //指向s3cfb_lcd结构体,主要定义屏幕的参数信息
	void		(*cfg_gpio)(struct platform_device *dev); //LCD相关GPIO配置
	int		(*backlight_on)(struct platform_device *dev); //打开背光
	int		(*backlight_onoff)(struct platform_device *dev, int onoff); //关闭背光
	int		(*reset_lcd)(struct platform_device *dev); //复位LCD
	int		(*clk_on)(struct platform_device *pdev, struct clk **s3cfb_clk); //LCD相关时钟打开
	int		(*clk_off)(struct platform_device *pdev, struct clk **clk); //LCD相关时钟关闭
};

/drivers/video/samsung/s3cfb.h中定义s3cfb_lcd结构体,本次移植需要直接添加s3cfb.h文件,并在mach-tq210.c文件中添加头文件#include <../../../drivers/video/samsung/s3cfb.h>

struct s3cfb_lcd {
	int	width;
	int	height;
	int	p_width;
	int	p_height;
	int	bpp;
	int	freq; //LCD刷新频率
	struct	s3cfb_lcd_timing timing; //LCD时序参数
	struct	s3cfb_lcd_polarity polarity; //LCD电平信号是否进行翻转

	void	(*init_ldi)(void); //初始化LDI
	void	(*deinit_ldi)(void);
};

下面,我们来看一下内核是如果使用这个结构体的。在/arch/arm/mach-s5pv210/mach-tq210.c中定义有:

static void EmbedSky_LCD_cfg_gpio(struct platform_device *pdev)
{
    int i;
    /* 将相应的IO引脚设置为LCD 相关功能 ,禁用上拉、下拉功能 */
    for (i = 0; i < 8; i++) {
        s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));
        s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);
    }

    for (i = 0; i < 8; i++) {
        s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));
        s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);
        }

    for (i = 0; i < 8; i++) {
        s3c_gpio_cfgpin(S5PV210_GPF2(i), S3C_GPIO_SFN(2));
        s3c_gpio_setpull(S5PV210_GPF2(i), S3C_GPIO_PULL_NONE);
    }
    for (i = 0; i < 4; i++) {
        s3c_gpio_cfgpin(S5PV210_GPF3(i), S3C_GPIO_SFN(2));
        s3c_gpio_setpull(S5PV210_GPF3(i), S3C_GPIO_PULL_NONE);
    }

    /* mDNIe SEL: why we shall write 0x2 ? */
    writel(0x2, S5P_MDNIE_SEL);

    writel(0xC0, S5PV210_GPF0_BASE + 0xc);
    /* 赋予最大的驱动能力 */ // 不懂
    /*writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
    writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
    writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
    writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);*/
}

#define S5PV210_GPD_0_0_TOUT_0  (0x2)
#define S5PV210_GPD_0_1_TOUT_1  (0x2 << 4)
#define S5PV210_GPD_0_2_TOUT_2  (0x2 << 8)
#define S5PV210_GPD_0_3_TOUT_3  (0x2 << 12)
static int EmbedSky_LCD_backlight_on(struct platform_device *pdev)
{
    int err;

    err = gpio_request(S5PV210_GPD0(0), "GPD0");

    if (err) {
        printk(KERN_ERR "failed to request GPD0 for "
                        "lcd backlight control\n");
        return err;
    }

    gpio_direction_output(S5PV210_GPD0(0), 1);

#if defined(CONFIG_FB_S3C_VGA640480) || defined(CONFIG_FB_S3C_VGA800600) || defined(CONFIG_FB_S3C_VGA1024X768)
    s3c_gpio_cfgpin(S5PV210_GPD0(0), /*S5PV210_GPD_0_0_TOUT_0*/1);
#else
    s3c_gpio_cfgpin(S5PV210_GPD0(0), S5PV210_GPD_0_0_TOUT_0); //设置GPD0_0为输出状态
    //s3c_gpio_setpull(S5PV210_GPD0(0), S3C_GPIO_PULL_UP);    // 使能GPD0_0引脚上拉电阻
    //gpio_set_value(S5PV210_GPD0(0), 1);                                       // 将GPD0_0设置成高电平
    //s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_SFN(2));         // 将GPD0_0设置为TOUT_0功能
#endif
    gpio_free(S5PV210_GPD0(0));

    return 0;
}

static int EmbedSky_LCD_backlight_off(struct platform_device *pdev, int onoff)
{
    int err;

    err = gpio_request(S5PV210_GPD0(0), "GPD0");
    if (err) {
        printk(KERN_ERR "failed to request GPD0 for "
                "lcd backlight control\n");
        return err;
    }
    //s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_OUTPUT);
    //s3c_gpio_setpull(S5PV210_GPD0(0), S3C_GPIO_PULL_DOWN);
    //gpio_set_value(S5PV210_GPD0(0), 0);

    gpio_direction_output(S5PV210_GPD0(0), 0);

    gpio_free(S5PV210_GPD0(0));
    return 0;
}

static struct s3cfb_lcd A70_TN92 = {
    .width        = 800, //LCD宽度
    .height        = 480, //LCD高度
    .p_width    = 152,
    .p_height    = 90,
    .bpp        = 32,  //像素位数
    .freq        = 100, //帧率 怎样获取?
//    .clkval_f    = 6,

    //时序,根据具体的LCD芯片手册设置
    .timing = {
        .h_fp    = 14, // 行数据传输完成后到下一个行同步信号到来的延迟 HFPD+1
        .h_bp    = 27, // 行同步信号完成后到行数据传输前的延迟 HFPD+1
        .h_sw    = 20, // 行同步信号的脉宽 HSPW +1
        .v_fp    = 22, // 帧数据传输完成后到下一个帧同步信号到来的延迟 VFPD+1
        .v_fpe    = 1,  // vertical front porch for even field, ?
        .v_bp    = 10, // 帧同步信号完成后到帧数据传输前的延迟 VBPD+1
        .v_bpe    = 1,  // vertical back porch for even field,  ?
        .v_sw    = 13, // 帧同步信号的脉宽 VSPW +1
    },

    //极性,通过对比s5pv210芯片手册中LCD控制器的时序与LCD的数据手册的时序
    .polarity = {
        .rise_vclk    = 0,
        .inv_hsync    = 1,
        .inv_vsync    = 1,
        .inv_vden    = 0,
    },
};

static struct s3c_platform_fb EmbedSky_LCD_fb_data __initdata = {
    .hw_ver             = 0x62,  //难道是hardware version ?
    .clk_name           = "sclk_fimd",
    .nr_wins             = 5, //number window? s5pv210有5个window
    .default_win         = CONFIG_FB_S3C_DEFAULT_WINDOW, //默认的window=0
    .swap                 = FB_SWAP_WORD | FB_SWAP_HWORD,
    .lcd                 = &A70_TN92,//记录LCD硬件参数信息
    .cfg_gpio            = EmbedSky_LCD_cfg_gpio,
    .backlight_on        = EmbedSky_LCD_backlight_on, //开启背光
    .backlight_onoff    = EmbedSky_LCD_backlight_off, //关闭背光
    .reset_lcd            = EmbedSky_LCD_reset_lcd, //重启LCD
};

在 arch/arm/mach-tq210/mach-tq210.c 中的 tq210_devices[]数组中添加了s5pv210_device_lcd:

static struct platform_device *tq210_devices[] __initdata = {
        ..        
        &s5pv210_device_lcd,
        ...
}
而在tq210_machine_init函数中将smdkv210_devices加入了platform,并将ek070tn93设置为s5pv210_device_lcd的platform data:
static void __init tq210_machine_init(void)
{
        ...

 //	s3c_fb_set_platdata(&tq210_lcd0_pdata);
#if defined(CONFIG_FB_TQ_AUTO_DETECT)
	EmbedSky_LCD_fb_data.lcd = get_s5pv210_fb();
#endif	
	s3c_fb_set_platdata(&EmbedSky_LCD_fb_data);//给平台设备设置私有数据,挂接fb平台设备私有数据
	platform_add_devices(tq210_devices, ARRAY_SIZE(tq210_devices));//平台设备注册

       ...
 }
         fb的驱动是基于platform平台总线的,所以需要提供platform_device(注册平台设备)和platform_driver(注册平台驱动)。前面讲的是平台驱动部分
   那么它对应的平台设备的注册在什么地方呢? 答案就是之前说的mach文件中,我这里是 arch\arm\mach-s5pv210\mach-tq210.c 这个文件。
   之前说了,这个文件中注册了很多的系统中可能用到的平台设备,将来写驱动的时候,只需要注册平台驱动即可,当然如果没有,可能就需要自己去添加。
   这个文件中将所有的平台设备结构体都放在一个 struct platform_device *类型的数组smdkc110_devices中,将所有定义好的platform_device结构体挂接到这个数组中去,
   在tq210_machine_init函数中将所有的平台设备都进行了注册。tq210_machine_init这个函数其实是被链接在Linux启动的各个初始化段中的某一个,所以当系统启动的时候,执行了初始化段中的函数时,tq210_machine_init函数就会被调用。















猜你喜欢

转载自blog.csdn.net/jerrygou/article/details/80072520