Linux LCD Driver (II)-framebuffer

Source: Internet
Author: User

2. Linux driver
2.1 framebuffer
Linux is working in protection mode, so user-mode processes cannot use the interrupt call provided in the graphics card BIOS as DOS to implement Direct Screen Writing and Lin imitation graphics card functions, the device that abstracts the explicit UX into framebuffer for the user-state process to directly write the screen. The hardware structure of the framebuffer mechanism module is abstracted. You can directly operate the video memory through the read/write operations of framebuffer. Users can regard framebuffer as an image showing the memory. After ing it to the process address space, they can directly perform read/write operations, and write operations can immediately respond to the screen. Such operations are abstract and unified. Users do not have to worry about the location of Physical video memory, page feed mechanism, and other details. These are all driven by the framebuffer device.

The main structure of framebuffer in Linux. Linux uses a layered structure for the convenience of developing the framebuffer program. Fbmem. C is in the center of the framebuffer Device Driver technology. It provides system calls for upper-layer applications and interfaces for specific hardware drivers at the next layer. These underlying hardware drivers need to use these interfaces to register themselves with the system kernel.

Fbmem. C provides common interfaces for all device drivers that support framebuffer to avoid repeated operations. The following describes some data structures of fbmem. C.

2.2 Data Structure
2.2.1 data structure of Linux framebuffer
In framebuffer, fb_info is the most important struct. It is the driver layer interface defined by Linux for frame buffer devices. It not only contains underlying functions, but also records device status data. Each frame buffer device corresponds to an fb_info structure. The main members of fb_info are as follows:
Struct fb_info {
Int node;
Struct fb_var_screeninfo var;/* Current Var */
Struct fb_fix_screeninfo fix;/* Current fix */
Struct fb_videomode * mode;/* Current Mode */

Struct fb_ops * fbops;
Struct device * device;/* This is the parent */
Struct device * dev;/* this is this fb device */

Char _ iomem * screen_base;/* virtual address */
Unsigned long screen_size;/* Amount of ioremapped VRAM or 0 */
............
};
The node member field indicates the specific framebuffer, which is actually the sub-device number of a framebuffer device. The fb_var_screeninfo struct records the user-modifiable display controller parameters, including the screen resolution and the number of bits per pixel. Xres in fb_var_screeninfo defines the number of vertices in a row on the screen, yres defines the number of vertices in a column on the screen, and bits_per_pixel defines how many bytes each vertex is represented. For other fields, see the following code annotations.
Struct fb_var_screeninfo {
_ U32 xres;/* visible resolution */
_ U32 yres;
_ U32 xoffset;/* offset from virtual to visible */
_ U32 yoffset;/* Resolution */
_ U32 bits_per_pixel;/* bits/pixel */
_ 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 hsync_len;/* length of horizontal sync */
_ U32 vsync_len;/* length of vertical sync */
............
};
In the fb_info struct, fb_fix_screeninfo records the parameters of the display controller that cannot be modified by the user, such as the physical address and length of the screen buffer. When you map a frame buffer device, the physical address of the buffer is obtained from fb_fix_screeninfo.
Struct fb_fix_screeninfo {
Char ID [16];/* identification string eg "TT builtin "*/
Unsigned long smem_start;/* Start of Frame Buffer MEM (physical address )*/
_ U32 smem_len;/* length of Frame Buffer mem */
Unsigned long mmio_start;/* Start of MEM mapped I/O (physical address )*/
_ U32 mmio_len;/* length of memory mapped I/O */
............
};
Another important domain of fb_info is fb_ops. It is an interface provided to the underlying device driver. When writing a character driver, we usually need to fill in a file_operations struct and register it with register_chrdev () to show Linux how to manipulate the driver. When writing a framebuffer, we need to follow the Linux framebuffer programming routine and fill in the fb_ops struct. This fb_ops is equivalent to the normal file_operations struct.
Struct fb_ops {
INT (* fb_open) (struct fb_info * info, int user );
INT (* fb_release) (struct fb_info * info, int user );
Ssize_t (* fb_read) (struct file * file, char _ User * Buf, size_t count, loff_t * PPOs );
Ssize_t (* fb_write) (struct file * file, const char _ User * Buf, size_t count,
Loff_t * PPOs );
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_setcmap) (struct fb_cmap * cmap, struct fb_info * info)
INT (* fb_mmap) (struct fb_info * info, struct vm_area_struct * VMA );
...............
}
The above struct can be seen Based on the function name, which is not described here. The overall structure of Linux framebuffer is given as a summary of this part.
 
Fig 2.2

2.2.2 LCD Data Structure in S3C2410
The s3c2410fb_info is defined in the driver of the LCD Device of S3C2410 to identify an LCD device. The structure is as follows:
Struct s3c2410fb_info {
Struct fb_info * FB;
Struct device * dev;
Struct s3c2410fb_mach_info * mach_info;
Struct s3c2410fb_hw regs;/* LCD hardware regs */
Dma_addr_t map_dma;/* physical */
U_char * map_cpu;/* virtual */
U_int map_size;
/* Addresses of pieces placed in raw buffer */
U_char * screen_cpu;/* virtual address of buffer */
Dma_addr_t screen_dma;/* physical address of buffer */
............
};
The member variable FB points to the fb_info structure described above, representing a framebuffer. Dev indicates the LCD device. Map_dma, map_cpu, and map_size are the memory addresses opened for LCD DMA. Screen_cpu and screen_dma point to the memory address mapped to the LCD controller. In addition, the regs identifies the registers of the LCD controller.
Struct s3c2410fb_hw {
Unsigned long lcdcon1;
Unsigned long lcdcon2;
Unsigned long lcdcon3;
Unsigned long lcdcon4;
Unsigned long lcdcon5;
};
This register corresponds to the hardware register. It is mainly used as an image of the actual register for the program to use.
This s3c2410fb_info also contains a member field of s3c2410fb_mach_info. It stores information related to the architecture, such as clock and gpio ports of LCD Devices. This struct is defined
Struct s3c2410fb_mach_info {
Unsigned char fixed_syncs;/* Do not update sync/border */
Int type;/* LCD types */
Int width;/* screen size */
Int height;
Struct s3c2410fb_val xres;/* screen info */
Struct s3c2410fb_val yres;
Struct s3c2410fb_val BPP;
Struct s3c2410fb_hw regs;/* LCD configuration REGISTERS */
/* Gpios */
Unsigned long gpcup;
Unsigned long gpcup_mask;
Unsigned long gpccon;
Unsigned long gpccon_mask;
............
};

Fig 2.3
Represents the overall structure of the S3C2410 driver, reflecting the relationships between struct

2.3 Main code structure and key code analysis
2.3.1 unified management of framebuffer drivers
Fbmem. c implements the intermediate layer of Linux framebuffer. Any framebuffer driver must be directed to fbmem during system initialization. c registration, that is, you need to call the register_framebuffer () function. In this process, the device driver information will be stored in the array named registered_fb, which is defined
Struct fb_info * registered_fb [fb_max];
Int num_registered_fb;
It is an array of the fb_info type, and num_register_fb stores the number of registered devices.
Let's analyze the register_framebuffer code.
Int register_framebuffer (struct fb_info * fb_info)
{
Int I;
Struct fb_event event;
Struct fb_videomode mode;

If (num_registered_fb = fb_max) Return-enxio;/* exceeds the maximum number */
Num_registered_fb ++;
For (I = 0; I <fb_max; I ++)
If (! Registered_fb [I]) break;/* Find the free array space */
Fb_info-> node = I;

Fb_info-> Dev = device_create (fb_class, fb_info-> device,
Mkdev (fb_major, I), "FB % d", I);/* create a device node for the device */
If (is_err (fb_info-> Dev )){
............
} Else {
Fb_init_device (fb_info);/* initialize and change the device */
}
............
Return 0;
}
The code above shows that when the framebuffer driver registers, it records the fb_info struct of the driver to the Global Array registered_fb, dynamically creates a device node, and initializes the device. Note that the secondary device Number of the created device node is the location where the driver information is stored in registered_fb, that is, the array subscript I. After the registration is completed, fbmem. c records the driver's fb_info. In this way, it is possible that fbmem. C can implement unified processing for all framebuffer drivers.

2.3.2 message distribution
Fbmem. c implements unified management of all framebuffer devices in the system. When a user tries to use a specific framebuffer, how does fbmem. c know which specific device driver should be called?
We know that Linux uniquely identifies a device by its primary device number and secondary device number. When different framebuffer devices register with fbmem. C, the primary device numbers assigned to them by the program are the same, and the secondary device numbers are different. So we can use the device number specified by the user to determine which framebuffer driver to call. The following describes the fb_open () function of fbmem. C. (Note: Generally, the framebuffer driver does not need to implement open functions. Here we only describe the function process .)

Static int fb_open (struct inode * inode, struct file * file ){
Int fbidx = iminor (inode );
Struct fb_info * Info;
Int res;
/* Get the real-driven function pointer */
If (! (Info = registered_fb [fbidx]) Return-enodev;
If (Info-> fbops-> fb_open ){
Res = Info-> fbops-> fb_open (Info, 1); // call the driver's open ()
If (RES) module_put (Info-> fbops-> owner );
}
Return res;
}
When you open a framebuffer device, the fb_open () function is called. The input inode is the device Number of the device to open, including the primary device and secondary device number. The fb_open function first obtains the device number by using the iminor () function, and then queries the Global Array registered_fb to obtain the fb_info information of the device. The operation function set fb_ops is stored here. In this way, we can call the fb_open () function of the specific driver to implement open operations. The following describes the call flowchart of an LCD-driven open () function.
 
Fig 2.4

2.3.3 Development Board S3C2410 LCD Driver Process
(1) initial LCD parameters are defined in the mach-smdk2410.c. Note that this is a global variable.
Static struct s3c2410fb_mach_info smdk2410_ LCD _cfg = {
. Regs = {
. Lcdcon1 = s3c2410_lcdconw.tft16bpp |
S3c2410_lcdconw.tft |
S3c2410_lcdcon1_clkval (7 ),
......
},
. Width = 240,. Height = 320,
. Xres = {. min = 240,. max = 240,. defval = 240 },
. Bpp = {. min = 16,. max = 16,. defval = 16 },
......
};
(2) Call the s3c2410fb_probe function during kernel initialization. Next we will analyze the work done by this function. First, we need to dynamically allocate the space of s3c2410fb_info.
Fbinfo = framebuffer_alloc (sizeof (struct s3c2410fb_info), & pdev-> Dev );
Point the mach_info field to smdk2410_ LCD _cfg IN THE mach-smdk2410.c.
Info-> mach_info = pdev-> Dev. platform_data;
Set the fix, VAR, and fops fields of the fb_info field.

Fbinfo-> fix. type = fb_type_packed_pixels;
Fbinfo-> fix. type_aux = 0;
Fbinfo-> fix. xpanstep = 0;

Fbinfo-> var. nonstd = 0;
Fbinfo-> var. Activate = fb_activate_now;
Fbinfo-> var. Height = mach_info-> height;
Fbinfo-> var. width = mach_info-> width;

Fbinfo-> fbops = & s3c2410fb_ops;
......
This function calls s3c2410fb_map_video_memory () to apply for the DMA memory, that is, the video memory.

FBI-> map_size = page_align (FBI-> FB-> fix. smem_len + page_size );
FBI-> map_cpu = dma_alloc_writecombine (FBI-> Dev, FBI-> map_size,
& FBI-> map_dma, gfp_kernel );

FBI-> map_size = FBI-> FB-> fix. smem_len;
.......
Set control registers and hardware registers.

Memcpy (& info-> regs, & mach_info-> regs, sizeof (Info-> regs ));
Info-> regs. lcdcon1 & = ~ S3c2410_lcdcon1_envid;
..........
Call the function s3c2410fb_init_registers () to write the initial value to the Register.

Writel (FBI-> regs. lcdcon1, s3c2410_lcdcon1 );
Writel (FBI-> regs. lcdcon2, s3c2410_lcdcon2 );

(3) When the user calls MMAP () ing memory, fbmem. c maps the configured memory area to the user.
Start = Info-> fix. smem_start;
Len = page_align (Start &~ Page_mask) + Info-> fix. smem_len );
Io_remap_pfn_range (VMA, VMA-> vm_start, off> page_shift,
VMA-> vm_end-VMA-> vm_start, VMA-> vm_page_prot );
......
In this way, the entire process from driver initialization to user calling is completed.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.