SEP0718 framebuffer驱动设计文档

第一部分:驱动的硬件配置部分:

Sep0718 处理器的 lcdc 控制器是带有普通的显示功能和 overlay 功能的,因此硬件配置上可以分成基本配置和额外的配置,基本的配置可以保证 lcdc 的正常运作,实现简单功能(单层的显示),额外配置主要包括对 overlay 的配置,从而实现多层叠加的效果:

基本配置:

基本配置包括:

1.     lcdc 的基本属性配置(极性, dma burst 长度, 屏幕 size ),在 bcr scr 寄存器中;

2.     Lcdc 的时序配置(行信号,帧信号,对比度控制),在 plcr pfcr pccr 寄存器中;

3.     Base 层的配置( buffer 起始地址,终止地址; base 层的显示位置, base 层的图像格式, RAW_IMAGE_WIDTH_0 DIS_IMAGE_WIDTH_0 ,在 bbsar bbear btpcr bbpcr bcr RAW_IMAGE_WIDTH_0 DIS_IMAGE_WIDTH_0 寄存器中。

 

注意:在对非 buffer 数据地址的 lcdc 参数重新配置时,一定要将 lcdc 使能关闭后才能重新配置。因此,简化为下面的初始化代码为:

    /* 关闭 lcdc 使能寄存器 */

write_reg(LCDC_ECR,0);

    write_reg(LCDC_BECR,0);

    write_reg(LCDC_W1ECR,0);

    write_reg(LCDC_W2ECR,0);

    write_reg(LCDC_CECR,0);

    /* 此函数中将配置时序及屏幕 size*/

    lcdc_set(800, 480, 30);

{

    unsigned int parameter = 0;

    unsigned int LCD_PCD,H_value,V_value;

    H_value = X + (H_WIDTH >> 26) +(H_WAIT1 >> 8) + H_WAIT2 + 7;

    V_value = Y + (V_WIDTH >> 26) +(V_WAIT1 >> 8) + V_WAIT2 + 2;

    LCD_PCD = (sysclk / Freq) / (H_value * V_value);

    if (LCD_PCD & 0x1 != 0)

       LCD_PCD = LCD_PCD - 1;

    else

       LCD_PCD = LCD_PCD - 2;

    if (LCD_PCD <= 2)

       LCD_PCD = 2;

    parameter = XMAX(X) | YMAX(Y);

    write_reg(LCDC_SCR,parameter);

   

    parameter = 0;

    parameter = parameter | INT_LEVEL | HB | PIXPOL | FLMPOL | LPPOL | CLKPOL | OEPOL | PCD;

    write_reg(LCDC_BCR,parameter);

   

    parameter = 0;

    parameter = H_WIDTH | H_WAIT1 | H_WAIT2;

    write_reg(LCDC_PLCR,parameter);

 

    parameter = 0;

    parameter = V_WIDTH | V_WAIT1 | V_WAIT2;

    write_reg(LCDC_PFCR,parameter);

   

    parameter = 0;

    parameter = SCR | CC_EN | PW;

    write_reg(LCDC_PCCR,parameter);

    //the reset config of the YUV display

}

    /*base 层的配置,包括起始地址, size */

BASE_CONFIGURE(0X40100000,800,800,0,0,799,479);

{

    U32 X1,X2,parameter;

   

    write_reg(LCDC_BBSAR,BBS_ADDR);

    write_reg(RAW_IMAGE_WIDTH_0,RAW_IMAGE_WIDTH);

    write_reg(DIS_IMAGE_WIDTH_0,DIS_IMAGE_WIDTH);

    X1 = (x1 << 16);

    X2 = (x2 << 16);

    parameter = X1 | y1;

    write_reg(LCDC_BTPCR,parameter);

    parameter = 0;

    parameter = X2 | y2;

    write_reg(LCDC_BBPCR,parameter);

}

lcdc_rgb_set(bpp16);

{

    U32 parameter;

   

    RGB_MODE = rgb_mode;

    parameter = read_reg(LCDC_BCR);

    parameter = parameter | BPIX_LAYER(RGB_MODE);

    write_reg(LCDC_BCR,parameter);

}

    /* 使能 lcdc*/

write_reg(LCDC_CECR,1);

write_reg(LCDC_W2ECR,1);

write_reg(LCDC_W1ECR,1);

write_reg(LCDC_BECR,1);

write_reg(LCDC_ECR,1);  

 

由于 lcd 控制器的硬件决定, lcdc 没有接受和发送的函数,需要更换显示内容时,只需要更换 lcdc buffer 地址即可, 改变 buffer 起始地址时,先将 LCDC_ACSR 寄存器置 0 ,配置完成后再次置 1

 

第二部分:驱动的软件设计部分 :

1.   驱动的框架介绍:

Framebuffer 驱动在本质上是一个字符型驱动,通过文件 /drivers/video/fbmem.c 对每个驱动进行抽象,而 fbmem.c 就是一个典型的字符型驱动,有 open close ioctl ;用户态应用程序将通过系统调用访问到 fbmem 的相应接口, fbmem.c 通过 ioctl 操作具体的 fb 驱动。

       对于一个 framebuffer 驱动而言,最重要的是下面几个参数:

1)   fb_var_screeninfo

这个结构描述了显示卡的特性:(这个结构体中很多参数和具体的液晶屏有关,因此有大量都是在液晶屏的配置文件中,用蓝色标示)
struct fb_var_screeninfo
{
__u32 xres; /* visible resolution */   //
可视区域
__u32 yres;
__u32 xres_virtual; /* virtual resolution */  //
虚拟区域,很多场合会用到,简单的意思就是我内存中定义的区间是比较大的,但是可视的仅仅是我的一部分,比如滚屏操作……
__u32 yres_virtual;

__u32 xoffset; /* offset from virtual to visible resolution */ //
可视区域的偏移
__u32 yoffset;

__u32 bits_per_pixel; /* guess what */  // 每一象素的 bit 数,这个参数不需要自己配置,而是通过上层在调用 checkvar 函数传递 bpp 的时候赋值的。
__u32 grayscale; /* != 0 Gray levels instead of colors *///
等于零就成黑白

// 通过 pixel per bpp 来设定 red green blue 的位置; pixel per bpp 可以通过 ioctl 设定

struct fb_bitfield red; /* bitfield in fb mem if true color, */ 真彩的 bit 机构
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;  
struct fb_bitfield transp; /* transparency */ 
透明

__u32 nonstd; /* != 0 Non standard pixel format */ 不是标准格式

__u32 activate; /* see FB_ACTIVATE_* */

__u32 height; /* height of picture in mm */ 内存中的图像高度
__u32 width; /* width of picture in mm */ 内存中的图像宽度

__u32 accel_flags; /* acceleration flags (hints) */ 加速标志

/* Timing: All values in pixclocks, except pixclock (of course) */

时序 -_- 这些部分就是显示器的显示方法了,和具体的液晶显示屏有关,在驱动中一般放在 具体液晶屏的配置文件
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */ 
水平可视区域
__u32 vsync_len; /* length of vertical sync */  
垂直可视区域
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 reserved[6]; /* Reserved for future compatibility */
备用-以后开发
};

2) fb_fix_screeninfon
这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改 ;比如 FrameBuffer 内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。

struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */ ID
unsigned long smem_start; /* Start of frame buffer mem */
内存起始物理地址,也就是 dma**
__u32 smem_len; /* Length of frame buffer mem */
内存大小,这个会根据是不是双 buffer ,是不是 virtual ,有变化。
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
插入区域?
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
没有硬件设备就为零
__u16 ypanstep; /* zero if no hardware panning */
设置成 1 就是标示我们可以在相应的方向移动显示 ,非常重要, android 中使用了
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
一行的字节表示
unsigned long mmio_start; /* Start of Memory Mapped I/O */ 内存映射的 I/O 起始
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */ I/O
的大小
__u32 accel; /* Type of acceleration available */  可用的加速类型
__u16 reserved[3]; /* Reserved for future compatibility */
};

3 struct fb_ops s3cfb_ops = {

       .owner           = THIS_MODULE,

       .fb_check_var = s3cfb_check_var, // 在上面的小节中提到对于一个 LCD 屏来说内核提供了两组数据结构来描述它,一组是可变属性( fb_var_screeninfo 描述),另一组是不变属性( fb_fix_screeninfo 描述)。对于可变属性,应该防止在操作的过程中出现超出法定范围的情况,因此内核应该可以调用相关函数来检测、并将这些属性固定在法定的范围内

       .fb_set_par     = s3cfb_set_par, // 用户应用程序可通过 ioctl FIX 参数进行重新配置,因此在进行这个之前首先会调用 check_var 保证参数在支持范围内。参数可改变的列表见 var 结构体,实际应用中主要是改变屏幕 bpp 和行的长度。

       .fb_blank = s3cfb_blank// 理论是关闭屏幕的操作(分别包括横向,纵向,整个屏) , 在实际中一般分为屏和背光都开,屏开背光关闭,屏和背光都关闭。

       .fb_pan_display      = s3cfb_pan_display,// FBIOPAN_DISPLAY linux 的注释里是 平移显示 的意思。怎么理解呢?就是按照 y 坐标平移显示缓存中的内容。调用 FBIOPAN_DISPLAY 时,会传一个 y 坐标偏移量 yoffset 给驱动,然后驱动会把当前显存的指针偏移 “yoffset X 屏幕宽度 X 位色字节数 个字节,这样就好像实现了图像的 y 坐标平移,也就是 平移显示 。当这个 yoffset 等于屏幕高度的时候,就实现了显存的切换。

       .fb_setcolreg   = s3cfb_setcolreg,

       .fb_fillrect      = cfb_fillrect,

       .fb_copyarea   = cfb_copyarea,

       .fb_imageblit  = cfb_imageblit,

       .fb_cursor       = soft_cursor,

       .fb_ioctl  = s3cfb_ioctl,

};

因此 sep0718fb.c 驱动所做的主要工作就是完成这三个大结构的配置填充,并且使之和第一部分的硬件配置挂钩在一起。

 

2.   代码接口:

a)          重要结构体说明:

综上, framebuffer 驱动主要涉及两方面,一方面涉及到 framebuffer 的参数配置,一方面涉及到 lcdc 硬件的配置,因此我设计了两个主要的结构体。

typedef struct {

struct fb_info         fb;

struct device          *dev;

struct clk        *clk;

struct resource        *mem;

void __iomem        *io;

unsigned int           win_id;

unsigned int           max_bpp;

unsigned int           max_xres;

unsigned int           max_yres;

/* raw memory addresses */

dma_addr_t           map_dma_f1;  /* physical */

u_char *         map_cpu_f1;   /* virtual */

unsigned int           map_size_f1;

 

/* addresses of pieces placed in raw buffer */

dma_addr_t           screen_dma_f1;      /* physical address of frame buffer */

u_char *         screen_cpu_f1;       /* virtual address of frame buffer */

 

unsigned int           lcd_offset_x;

unsigned int           lcd_offset_y;

unsigned int   pseudo_pal[16];

} sep0718fb_info_t ;

sep0718fb_info_t 是关于 framebuffer 参数配置的结构体,里面主要包含了 fb_info 结构体(这个结构体中包含了上面提到的三个重要的参数 var fix fb_ops )。

 

typedef struct

{

/* Screen size */

int width;

int height;

 

/* Screen info */

int xres;

int yres;

 

/* Virtual Screen info */

int xres_virtual;

int yres_virtual;

int xoffset;

int yoffset;

 

/* OSD Screen size (overlay 1)*/

int osd_width;

int osd_height;

 

/* OSD Screen info */

int osd_xres;

int osd_yres;

 

/* OSD Screen info */

int osd_xres_virtual;

int osd_yres_virtual;

 

int bpp;

int bytes_per_pixel;

unsigned long pixclock;

 

 

/* lcd configuration registers */

struct sep0718fb_hw regs;

 

int hsync_len;

int left_margin;

int right_margin;

int vsync_len;

int upper_margin;

int lower_margin;

int sync;

 

int cmap_grayscale:1;

int cmap_inverse:1;

int cmap_static:1;

int unused:29;

 

/* backlight info */

int backlight_min;

int backlight_max;

int backlight_default;

 

int vs_offset;

int brightness;

int palette_win;

int backlight_level;

int backlight_power;

int lcd_power;

}sep0718_lcdc_info_t ;

sep0718_lcdc_info_t 是关于 0718 lcdc 硬件的结构体。里面包含了 0718 的寄存器映射列表,用于和 sep0718fb_info_t 值交互的一些参数。

 

3.   驱动详细说明:

Sep0718fb 驱动所采用的架构是基于 platform 的形式。 Probe 函数是驱动的初始化函数, remove 是驱动的卸载函数, suspend resume 是驱动的挂起和恢复函数,在电源管理的时候会用到。

 

Probe 函数是整个系统的核心 ,由于之前提到由于 lcdc 的使用流程更多的是对寄存器的配置,因此 probe 对于整个驱动非常重要。 Probe 主要实现了两件事情,完成了对硬件的初始化以及对 framebuffer 结构的申请初始化注册。

硬件的初始化分布在 probe 中的两个地方:

1. if (index == 0)

               sep0718fb_init_hw(&sepfb_info[index]);

这里完成了 lcdc 的最基本的配置,比如时序,极性,分辨率等,因此这部分的内容是跟具体的屏幕有关的,因此这个函数是需要针对不同的屏幕实现的,具体的代码在 fb800_480.c.

2. ret = sep0718fb_init_registers(&sepfb_info[index]);

由于我们的 lcdc overlay 多层架构的,因此将针对不同的层分别进行配置。

       framebuffer 结构体的初始化主要分布在 probe 中的两个函数:

sep0718fb_init_fbinfo(&sepfb_info[index], driver_name, index);// 完成了对大部分 fb 参数的配置

       /* Initialize video memory */

       ret = sep0718fb_map_video_memory(&sepfb_info[index]);// 主要完成了对缓冲区的配置。

 

struct fb_ops sep0718fb_ops 是整个驱动在初始化结束后会涉及的内容, 由于在 probe 中会将 sep0718fb_ops 结构体注册到内核中。因此在驱动完成初始化后,其实就是这个结构体在起作用,因此下面讲一下这个结构体所做的工作。在这个结构体中具体是驱动设计到的函数有:

fb_check_var: 在上面的小节中提到对于一个 LCD 屏来说内核提供了两组数据结构来描述它,一组是可变属性( fb_var_screeninfo 描述),另一组是不变属性( fb_fix_screeninfo 描述)。对于可变属性,应该防止在操作的过程中出现超出法定范围的情况,因此内核应该可以调用相关函数来检测、并将这些属性固定在法定的范围内。我们在这里的实现主要是针对 bits_per_pixel 的。

 

fb_set_var 用户应用程序可通过 ioctl FIX 参数进行重新配置,因此在进行这个之前首先会调用 check_var 保证参数在支持范围内。参数可改变的列表见 var 结构体,实际应用中主要是改变屏幕 bpp 和行的长度。这里需要注意的一个地方时在 set_var 函数的内部我们是通过 active 函数来使寄存器重新配置的,对寄存器参数进行重新配置时一定要对 lcdc 先关闭,然后配完了再使能。

 

fb_pan_display FBIOPAN_DISPLAY linux 的注释里是 平移显示 的意思。怎么理解呢?就是按照 y 坐标平移显示缓存中的内容。调用 FBIOPAN_DISPLAY 时,会传一个 y 坐标偏移量 yoffset 给驱动,然后驱动会把当前显存的指针偏移 “yoffset X 屏幕宽度 X 位色字节数 个字节,这样就好像实现了图像的 y 坐标平移,也就是 平移显示 。当这个 yoffset 等于屏幕高度的时候,就实现了显存的切换。由于 android 中的要求是对 y 方向平移显示,所以我们在这里的实现也是平移显示的。注意是 y 平移还是 x 平移是在 sep0718fb_init_fbinfo 初始化函数中的       finfo->fb.fix.ypanstep = 1; 所决定的。

 

fb_ioctl ioctl 函数主要是为驱动实现一些 framebuffer 架构没有包含的一些特殊的特性,用户应用程序可以通过 ioctl 来操作这些特殊的操作。 Sep0718 overlay alpha blending color key 等功能就是通过此处实现的。

第三部分:驱动的内核配置及代码分布:

1: 文件位置说明(具体文件, kconfig makefile

整个驱动主要分为 3 个文件,均在 /drivers/video/sep0718 目录: sep0718_fb.c 是整个 framebuffer 的最主要的组成,包含了 fb 的所有操作,硬件配置; fb800_480.c 是具体液晶屏的配置文件; sep0718_fb.h fb 的头文件。

 

由于驱动是 platform 结构,因此还有一部分关于 device 设备描述的代码在 /arch/arm/mach_sep0718/board.c 中。

 

相应 kconfig 代码位置: /drivers/video/kconfig line245-295

comment "SEP0718 Frame buffer hardware drivers config"

depends on FB

 

config FB_SEP0718

tristate "SEP0718 frame buffer support "

depends on FB && ARCH_SEP0718

select FB_CFB_FILLRECT

select FB_CFB_COPYAREA

select FB_CFB_IMAGEBLIT

help

          Frame buffer driver for SEP0718 based boards.

 

choice

prompt "SEP0718 LCDC TYPE"

depends on FB_SEP0718

default FB_SEP0718_800_480

 

config FB_SEP0718_800_480

bool "800*480 lcd support "

depends on FB_SEP0718

 

endchoice

 

 

choice

prompt "SEP0718 LCDC COLOR TYPE"

depends on FB_SEP0718

default FB_SEP0718_BPP_16

 

config FB_SEP0718_BPP_16

bool "16 bpp"

depends on FB_SEP0718

 

config FB_SEP0718_BPP_18

bool "18 bpp unpacked"

depends on FB_SEP0718

 

config FB_SEP0718_BPP_24

bool "24 bpp unpacked"

depends on FB_SEP0718

 

endchoice

 

config FB_SEP0718_NUM

    int "Number of Framebuffers"

    depends on FB_SEP0718

    default "1"

 

config FB_SEP0718_VIRTUAL_SCREEN

bool "sep0718 virtual screen support"

depends on FB_SEP0718

 

comment "********************"

depends on FB

 

相应的 makefile 的位置, drivers/video/sep0718/makefile

obj-$(CONFIG_FB_SEP0718) += sep0718_fb.o

obj-$(CONFIG_FB_SEP0718_800_480) += fb800_480.o

 

通过这种架构的实现,以后增加不同型号液晶屏,只需直接增加一个液晶屏的配置文件,可以完全拷贝 fb800_480.c 的代码,只需对文件开始处的宏定义按照所用的屏进行修改即可(当然也需要按照 fb800_480.c, kconfig makefile 部分进行修改,即按照红色部分进行增加)。

 

2:  make menuconfig 选择:

简单驱动选择路径:

一次选上framebuffer驱动,控制台输出,bootuplogo


http://www.cublog.cn/u3/99423/showart_2260333.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值