LCD frame Introduction

Source: Internet
Author: User

Revision history

Date issue description author

<04/15/2010> <0.1> LCD Architecture Analysis wylhistory

Directory

1. Summary 3

2. Introduction 3

3. Train of Thought 3

4. initialize 4

4.1 device and driver registration 4

4.2 run probe function 6

5. User procedure 8

5.1 upper layer software use process 8

5.2 drive processing logic 9

6. Conclusion 12

7. Not discussed 12

 

1. Summary

Analyze the architecture of the LCD driver on the pxa310 platform;

2. Introduction

Every time I change the LCD screen, it will go through a painful process. So I want to take a look at its architecture so that I can provide a simple guide in the future integration process;

Hardware Platform: pxa310

Software Platform: Android 2.1

Kernel: 2.6.29

3. Ideas

For Driver integration, one of my original ideas is that the driver contains two aspects,

A) Hardware Management;

B) Provide the upper interface, that is, to the application layer interface;

From the perspective of Hardware Management, any module, such as WiFi, Bluetooth, and LCD, must be connected to our CPU for communication, the conclusion from the above sentence is as follows:

(The module works normally) = (the module itself works normally) + (the module controller works normally) + (the interface works normally );

That is to say, to make a driver work, we must consider these three aspects, which correspond to the LCD driver we will discuss, which means:

A) the LCD screen itself needs to be correctly initialized to enter the normal working state (this current LCD screen does not provide feedback );

B) the LCD controller should be correctly initialized to enter a normal working state (this can be verified by printing registers );

C) the interface must be correctly configured to implement communication between the LCD controller and the LCD screen. The interfaces usually contain command interfaces and data interfaces, our command interface is SPI (analog SPI protocol through gpio port), so we need to initialize the SPI interface. Data interfaces are generally divided into RGB interfaces and CPU interfaces, RGB is usually 16 lines. Sometimes it needs to be extended to adapt to the pin of the screen, while the CPU usage is 8. We also need to expand here, for example, if a high-level extension is used, for example, if we transmit a five-digit red, then our M4-M0 bit is connected to the opposite side's P7-P3, the P0-P2 of the other party is connected to our M4-m2 (this can be verified by an oscilloscope );

For the above interface, each driver usually has a unified interface, for example:

Open, close, ioctl, write, read, MMAP, of course, different drivers may only be implemented. For LCD drivers, the most important thing is open, ioctl, and MMAP, we can look at its code logic later;

The following is a detailed analysis from the initialization and upper-layer usage perspectives:

4. Initialization

4.1 device and driver registration

 

Figure 4.1 Registration of devices and drivers

Some drivers and drivers are registered in different places, but LCD drivers are placed in the same function, in pxafb_init in pxafb. C;

By convention, the device is first registered, and then the driver is registered. We can see that the device registration is finally in the pxa_register_device function to see its code:

Void pxa_register_device (struct platform_device * Dev, void * Data)

{

Int ret;

Dev-> Dev. platform_data = data;

Ret = platform_device_register (Dev );

If (RET)

Dev_err (& Dev-> Dev, "unable to register device: % d/N", RET );

}

The most important here is the blue sentence above, which saves the passed info parameter to platform_data. We can see that many operations require the participation of this data structure, it can even be said that this data structure is very important for the LCD driver. If you don't look at it, it is not human feelings, as shown below:

Struct pxafb_mach_info lin2008_lcdpar = {

. Modes = lin2008_modes,

. Num_modes = 1,

. Lccr0 = lccr0_act,

. Lc32a = lccr3_stall | lccr3_outenh,

};

Here, let's take a look at the lccr0 value. The notes are as follows:

# Define lccr0_act (lccr0_pas * 1)/* Active Display (TFT )*/

Simply put, the screen type is defined;

The value of lc31:

# Define lccr3_stall (1 <28)/* stall pixel clock if underrun occurs */

# Define lccr3_outenh (lccr3_oep * 0)/* output enable active high */

If no data is transmitted, the pixel clock is stopped;

The second line enables output and is highly effective;

Here I am most concerned about lin2008_modes. Let's take a look at its content:

Static struct pxafb_mode_info lin2008_modes [] = {

[0] = {

//. Pixclock = 68296, // consider for 812*492*36 frame

//. Pixclock = 48344, // consider for 812*492*60 Frame

. Pixclock = 40977, // consider for 504*807*60

//. Pixclock = 68296, // consider for 504*807*36

. Xres = 480,

. Yres = 800,

. Bpp = 16,

. Hsync_len = 7,

. Left_margin = 8,

. Right_margin = 16,

. Vsync_len = 1,

. Upper_margin = 4,

. Lower_margin = 3,

. Sync = 0,

},

};

The information here is very important, one by one:

Pixclock: this refers to the reciprocal of the clock frequency. Otherwise, the calculation is not correct, and there is no row synchronization or frame synchronization, 40997 = (1 <12) /(480 + 8 + 16) × (800 + 4 + 3) × 60; that is to say, this value is equal to the resolution of x plus the product of the front corridor + the Back corridor and the resolution of Y plus the value after the upper corridor and the Lower corridor;

OK. Now we will go back to the previous device registration, that is, the dev member in the pxa_register_device function, as shown below:

Struct platform_device pxa_device_fb = {

. Name = "pxa2xx-fb ",

. ID =-1,

. Dev = {

. Dma_mask = & fb_dma_mask,

. Coherent_dma_mask = 0 xffffffff,

},

. Num_resources = array_size (pxafb_resources ),

. Resource = pxafb_resources,

};

The only thing to pay attention to is this resource, as shown below:

Static struct resource pxafb_resources [] = {

[0] = {

. Start = 0x44000000,

. End = 0x4400ffff,

. Flags = ioresource_mem,

},

[1] = {

. Start = irq_ LCD,

. End = irq_ LCD,

. Flags = ioresource_irq,

},

};

That is to say, the address range of the registers related to the LCD controller is between 0x44000000 -- 0x4400ffff;

Now that the device has been registered, let's look at the driver registration,

Platform_driver_register (& pxafb_driver). The most important thing here is pxafb_driver. Let's take a look at its value:

Static struct platform_driver pxafb_driver = {

. Probe = pxafb_probe,

# Ifdef config_pm

. Suspend = pxafb_suspend,

. Resume = pxafb_resume,

# Endif

. Driver = {

. Name = "pxa2xx-fb ",

},

};

4.2 execution of probe Functions

According to the principles of platform equipment, as long as the name is the same, you can get married. The so-called "Jin Feng Yu Lu meets each other and calls the probe function"

So the probe function, pxafb_probe, is called. This function is quite long. To sum up, at least the following tasks are done:

1. Call lcdchip_init to initialize the LCD screen;

2. Retrieve INF = Dev-> Dev. platform_data. This is previously assigned when the device was registered. It is the source of the raw data, and many subsequent operations are related to it;

3. A bunch of parameter checks are not discussed;

4. Construct framebuffer information. For the pxa310 architecture, there are three layers. Therefore, different layers have different framebuffer information. However, the initial information is from the previous platform_data, we can understand the actions in this step as I have summarized the preparations to provide the correct interface. Here we will fill in the VaR information and fix information we see at the application layer; the two statements must be described separately: FBI-> FB. fbops = & pxafb_ops ;......; Init_work (& FBI-> task, pxafb_task); the previous one sets the function operation set, which will be used after one sentence of initializing a work pxafb_task;

5. Use the pxafb_map_video_memory function to allocate the framebuffer space. The space length is xres * yres * BPP/2 (if it is Android, it will be multiplied by 2 because it is dual-buffering) + pagesize, this pagesize is usually used to store the DMA descriptor. If you want to adjust the framebuffer size seen in the upper layer, You need to modify it here. Of course, the smem_len returned to the upper layer must also be modified;

6. Set the interrupt processing function, which is usually used for debugging;

7. Use pxafb_set_par to set the address of the DMA Descriptor and the content in the descriptor. If a register is changed, use pxafb_schedule_work (FBI, c_reenable) to start the work (pxafb_task) set in step 1. Note that it is not run immediately;

8. You can use register_framebuffer (& FBI-> FB) to register this framebuffer information. In essence, you can set registered_fb [Index] to point to the previously allocated framebuffer information, so that it can be obtained later;

9. Call set_ctrlr_state (FBI, c_enable) and enable controller through pxafb_enable_controller. Here, the values previously assigned in step 1 are placed in the specific register;

Now let's take a look at the pxafb_ops in Step 1:

Static struct fb_ops pxafb_ops = {

. Owner = this_module,

. Fb_check_var = pxafb_check_var,

. Fb_set_par = pxafb_set_par,

. Fb_setcolreg = pxafb_setcolreg,

. Fb_fillrect = cfb_fillrect,

. Fb_copyarea = cfb_copyarea,

. Fb_imageb.pdf = cfb_imageb.pdf,

. Fb_blank = pxafb_blank,

. Fb_mmap = pxafb_mmap,

. Fb_pan_display = pxafb_pan_display,

# Ifdef config_fb_pxa_minilcd

. Fb_ioctl = pxafb_minilcd_ioctl,

# Else

. Fb_ioctl = pxafb_ioctl,

# Endif

};

Let's take a look at this pxafb_task:

Static void pxafb_task (struct work_struct * Work)

{

Struct pxafb_info * FBI =

Container_of (work, struct pxafb_info, task );

U_int state = xchg (& FBI-> task_state,-1 );

Set_ctrlr_state (FBI, State );

}

In this case, first obtain the FBI-> task_state and put it into the state. It is estimated that an xchg operation is performed to achieve mutually exclusive access; then, the real LCD controller register operation is implemented through set_ctrlr_state;

In summary, the original parameter settings are in static struct pxafb_mode_info lin2008_modes and must be filled in correctly. It is estimated that the parameters must be modified for different screens, and then the Controller's enable, this will be passed to the register only when set_ctrlr_state is required. That is to say, before this, you must ensure that the related values have been correctly assigned to framebuffer info, note that the content of the descriptor is filled in. The structure is as follows:

Struct pxafb_dma_descriptor {

Unsigned int fdadr;

Unsigned int fsadr;

Unsigned int fidr;

Unsigned int ldcmd;

};

Fdadr: the address of the next descriptor. Note that this is the address of DMA, which is the physical address on our platform. Generally, it can point to the address of this structure, in this way, a ring can be formed to achieve constant refreshing;

Fsadr: this is the data address, that is, the data written at the application layer. for Android platforms, this address may be the starting address of framebuffer, it may also be the starting address + xres * yres * BPP/2;

Fidr, this value is not used very much, we fill in 0;

Ldcm, that is, the length of the data. If you want to screen a screen, for the 800x480 resolution, it is * 2;

5. User procedure

5.1 upper-layer software use process

The Code is as follows:

Static int fb_post (struct framebuffer_device_t * Dev, buffer_handle_t buffer)

{

If (private_handle_t: Validate (buffer) <0)

Return-einval;

Fb_context_t * CTX = (fb_context_t *) Dev;

Private_handle_t const * HND = reinterpret_cast <private_handle_t const *> (buffer );

Private_module_t * m = reinterpret_cast <private_module_t *> (

Dev-> common. Module );

If (m-> currentbuffer ){

M-> base. Unlock (& M-> base, M-> currentbuffer );

M-> currentbuffer = 0;

}

If (HND-> flags & private_handle_t: priv_flags_framebuffer ){

M-> base. Lock (& M-> base, buffer,

Private_module_t: priv_usage_locked_for_post,

0, 0, M-> info. xres, M-> info. yres, null );

Const size_t offset = HND-> base-m-> framebuffer-> base;

M-> info. Activate = fb_activate_vbl;

M-> info. yoffset = offset/m-> finfo. line_length;

If (IOCTL (m-> framebuffer-> FD, fbioput_vscreeninfo, & M-> info) =-1 ){

LogE ("fbioput_vscreeninfo failed ");

M-> base. Unlock (& M-> base, buffer );

Return-errno;

}

M-> currentbuffer = buffer;

} Else {

// If we can't do the page_flip, just copy the buffer to the front

// Fixme: Use copybit Hal instead of memcpy

Void * fb_vaddr;

Void * buffer_vaddr;

M-> base. Lock (& M-> base, M-> framebuffer,

Gralloc_usage_sw_write_rarely,

0, 0, M-> info. xres, M-> info. yres,

& Fb_vaddr );

M-> base. Lock (& M-> base, buffer,

Gralloc_usage_sw_read_rarely,

0, 0, M-> info. xres, M-> info. yres,

& Buffer_vaddr );

Memcpy (fb_vaddr, buffer_vaddr, M-> finfo. line_length * m-> info. yres );

M-> base. Unlock (& M-> base, buffer );

M-> base. Unlock (& M-> base, M-> framebuffer );

}

Return 0;

}

This function will call FB-> post (FB, handle) to trigger the execution of this function in the postframebuffer of surfaceflinger layer to the queuebuffer of the OpenGL layer to the nativewindow layer;

From the driver's point of view, it is most concerned with the ioctl fbioput_vscreeninfo. Let's look at the corresponding processing logic of the driver;

5.2 drive processing logic

As shown in:

 

The upper-layer IOCTL triggers the execution of the fb_ioctl, first, retrieve the previously registered framebuffer information from registerd_fb [fbidx] (for fb0, it is registered through register_framebuffer in the probe function ); then pass this information through fb_set_var. var indicates the data information transmitted from the upper layer. This function is also quite long, But I posted it and removed a write error:

Int

Fb_set_var (struct fb_info * info, struct fb_var_screeninfo * var)

{

Int flags = Info-> flags;

Int ret = 0;

............................

.............................

If (Var-> activate & fb_activate_force) |

Memcmp (& info-> var, VAR, sizeof (struct fb_var_screeninfo ))){

U32 activate = var-> activate;

If (! Info-> fbops-> fb_check_var ){

* Var = Info-> var;

Goto done;

}

Ret = Info-> fbops-> fb_check_var (VAR, Info );

If (RET)

Goto done;

If (Var-> activate & fb_activate_mask) = fb_activate_now ){

Struct fb_videomode mode;

If (Info-> fbops-> fb_get_caps ){

Ret = fb_check_caps (Info, VAR, activate );

If (RET)

Goto done;

}

Info-> Var = * var;

If (Info-> fbops-> fb_set_par)

Info-> fbops-> fb_set_par (Info );

Fb_pan_display (Info, & info-> var );

Fb_set_cmap (& info-> cmap, Info );

Fb_var_to_videomode (& mode, & info-> var );

If (Info-> modelist. Prev & info-> modelist. Next &&

! List_empty (& info-> modelist ))

Ret = fb_add_videomode (& mode, & info-> modelist );

If (! RET & (flags & fbinfo_misc_userevent )){

Struct fb_event event;

Int evnt = (activate & fb_activate_all )?

Fb_event_mode_change_all:

Fb_event_mode_change;

Info-> flags & = ~ Fbinfo_misc_userevent;

Event.info = Info;

Event. Data = & mode;

Fb_notifier_call_chain (evnt, & event );

}

}

}

Done:

Return ret;

}

A) check the validity of the input parameter through pxafb_check_var;

B) Call Info-> fbops-> fb_set_par (Info), that is, pxafb_set_par, to calculate the new DMA transmission-related parameters. Call pxafb_activate_var to synchronize new data including rows and frames, the front corridor, Back corridor, and back corridor also include updates to DMA descriptor content stored in the FBI-> Reg * member, and then triggers the pxafb_task scheduling. Note that the task has not been executed yet;

C) fb_pan_display-> Info-> fbops-> fb_pan_display (VAR, Info). Finally, pxafb_pan_display is called. The main function is pxafb_schedule_work (FBI, c_change_dma_base ); note that this status is set later. That is to say, the status set earlier in fb_set_par will be overwritten by this status, then, when executing pxafb_task, the obtained status will be c_change_dma_base;

When this pxafb_task is scheduled to run, it will go to the branch of c_change_dma_base, as shown below:

Static void pxafb_task (struct work_struct * Work)

{

Struct pxafb_info * FBI =

Container_of (work, struct pxafb_info, task );

U_int state = xchg (& FBI-> task_state,-1 );

Set_ctrlr_state (FBI, State );

}

Void set_ctrlr_state (struct pxafb_info * FBI, u_int state)

{

......

Case c_change_dma_base:

FBI-> dmadesc_fbhigh_cpu-> fsadr = FBI-> screen_dma +

(FBI-> FB. var. xres * FBI-> FB. var. yoffset */

FBI-> FB. var. bits_per_pixel/8 );

Break;

......

}

We can see that it is basically modifying the fsadr address, and its value. We can see that it is the starting address of the actual data. In this way, when the next DMA data is transferred, the data will be obtained from this address, thus implementing the dual-buffer mechanism on the Android platform;

6. Summary

A) modify the resolution. That is to say, if the screen itself supports this new resolution, you only need to modify the static struct pxafb_mode_info lin2008_modes struct. xres, yres is the root of values in many subsequent data structures. If you need to simulate 320x133 on a screen that only supports x snapshot, you also need to modify the front corridor and Back corridor to extend to the corresponding pixelclock;

B) The real controller settings are made in set_ctrlr_state, and these values are all set in pxafb_activate_var, that is, if you want to trick the controller into implementing the difference between the actual value and the value seen in the upper layer, I would like to do hack here;

C) if the screen is not bright, I want to first consider the voltage problem. If the voltage is normal, consider the other four lines, clock, hsync, vsync, and De line, if this is normal, check whether the data line is normal. You can see this through the oscilloscope. Of course, correct screen Initialization is a prerequisite, and the screen cannot provide feedback, this is also the most painful part of LCD Driver debugging, because you cannot determine whether the initialization is correct;

D) You can determine the sending status of each frame by opening the interrupt. Of course, the printed information in the frame is not completely valid, but it can be used as a reference. If it is always printed, at least it can be noted that the data is constantly being refreshed;

E) If there is a difference between the displayed result and the expected result, you can capture the content conversion layer image of framebuffer for comparison; of course, you can also use a cat image to get to framebuffer to determine whether the driver is working. If Cat works at the beginning, after a certain period of time, cat does not work, it indicates that some hardware configurations have been changed in this process. You need to check whether other modules are in conflict with your settings;

7. Not discussed

1. The detailed settings of the controller register are not analyzed. I think this should be detailed against spec;

2. the initialization of the LCD screen is not carefully read. I think it is related to the screen itself;

3. Others;

Note:

By wylhistory

Contact: wylhistory@gmail.com

Address: http://blog.chinaunix.net/u2/67984/showart_2237233.html

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.