// Set the register 0xf800_0130 (Video interrupt control 0 register) [16: 15] bits to 01 = vsync, which may be 00 = back porch, 10 = active Int s3cfb_set_vsync_interrupt (struct s3cfb_global * Ctrl, int enable) // Set the register 0xf800_0130 enable or disable to [12] and [0], respectively, indicating // Intfrmen [12] specifies the video frame interrupt enable control bit. // 0 = disables video frame interrupt // 1 = enables video frame interrupt // Note: this bit is meaningful when inten is high // Inten [0] specifies the video interrupt enable control bit. // 0 = disables video interrupt // 1 = enables video interrupt Int s3cfb_set_global_interrupt (struct s3cfb_global * Ctrl, int enable) Static int s3cfb_init_global (void) { Fbdev-> output = output_rgb; Fbdev-> rgb_mode = mode_rgb_p; Fbdev-> wq_count = 0; Init_waitqueue_head (& fbdev-> WQ ); Mutex_init (& fbdev-> lock ); S3cfb_set_output (fbdev ); // Set the [28:26] bit of the register 0xf800_0000, which indicates // Determines the output format of Video Controller. // 000 = RGB interface // 001 = Reserved // 010 = indirect i80 interface for ldi0 // 011 = indirect i80 interface for ldi1 /// 100 = WB interface and RGB interface /// 101 = Reserved // 110 = WB interface and i80 interface for ldi0 // 111 = WB interface and i80 interface for ldi1 // Set the register 0xf800_0008 [] [] bits, meaning // Reserved [15: 14] reserved. // Note: This bit shocould be 1. // Tvformatsel [13:12] specifies the output format of YUV data. // 00 = Reserved // 01 = yuv422 // 1X = yuv444 // Set all four digits to 0 S3cfb_set_display_mode (fbdev ); // Set the [18] [17] bits of the register 0xf800_0000. The meanings of the two are as follows: // Rgspsel [18] selects display mode (vidout [1:0] = 2 'b00 ). // 0 = RGB parallel format // 1 = RGB serial format // Selects the display mode (vidout [1:0]! = 2 'b00 ). // 0 = RGB parallel format // Pnrmode [17] controls inverting rgb_order (@ vidcon3 ). // 0 = normal: rgborder [2] @ vidcon3 // 1 = invert :~ Rgborder [2] @ vidcon3 // Note: this bit is used for the previous version of fimd. You do // Not have to use this bit if you use rgb_order @ vidcon3 // Register. // This is set to 0 mode_rgb_p = 0, Mode_bgr_p = 1, Mode_rgb_s = 2, Mode_bgr_s = 3, S3cfb_set_polarity (fbdev ); // Set the register xf800_0004, which is in the [7], [6], [5], and [4] bits. // Ivclk [7] controls the polarity of the vclk active edge. // 0 = video data is fetched at vclk falling edge // 1 = video data is fetched at vclk rising edge // Ihsync [6] specifies the hsync pulse polarity. // 0 = normal // 1 = inverted // Ivsync [5] specifies the vsync pulse polarity. // 0 = normal // 1 = inverted // Ivden [4] specifies the vden signal polarity. // 0 = normal // 1 = inverted S3cfb_set_timing (fbdev ); // Set the 32-bit 0xf800_0010 register to indicate // Vbpvdf [31: 24] vertical back porch specifies the number of inactive lines at // Start of a frame after vertical synchronization period. (Only for even // Field of yvu Interface // Vbpd [23: 16] vertical back porch specifies the number of inactive lines at // Start of a frame after vertical synchronization period. // Vfpd [15: 8] vertical front porch specifies the number of inactive lines at the end // Of a frame before vertical synchronization period. // Vspw [7:0] vertical sync pulse width determines the High-Level Width of vsync // Pulse by counting the number of inactive lines. // Set the register 0xf800_0014, which is expressed by 32 bits. // Vfpvdf [31: 24] vertical front porch specifies the number of inactive lines at the end // Of a frame before vertical synchronization period. (only for the even // Field of yvu interface ). // Hbpd [23: 16] horizontal back porch specifies the number of vclk periods // Between the falling edge of hsync and start of active data. // Hfpd [15: 8] horizontal front porch specifies the number of vclk periods // Between the end of Active Data and rising edge of hsync. // Hspw [7:0] horizontal sync pulse width determines the High-Level Width // Hsync pulse by counting the number of vclk. S3cfb_set_ LCD _size (fbdev ); // Set the register 0xf800_0018, which is expressed by the 22-bit valid bits. // Lineval [21:11] determines the vertical size of display. In the interlace mode, // (Lineval + 1) shocould be even. // Hozval [10: 0] determines the horizontal size of display. // Note: hozval = (horizontal display size)-1 and lineval = (vertical display size)-1. Return 0; } Let's continue to look at the probe function. ///////////////////////////////////////// Lcddebug ("% s, s3cfb init Global \ n", _ FUNC __); S3cfb_init_global (); Lcddebug ("% s, s3cfb init global done \ n", _ FUNC __);
# If! Defined (config_fb_89c_dummylcd) S3cfb_display_on (fbdev ); // Set the [1] [0] of the register 0xf800_0000 to, which respectively represent // ENVID [1] enables/disables video output and logic immediately. // 0 = disables the video output and display control signal. // 1 = enables the video output and display control signal // Envid_f [0] enables/disables video output and logic at current frame end. // 0 = disables the video output and display control signal. // 1 = enables the video output and display control signal. // * If this bit is set to "on" and "off", then "H" is read and video // Controller is enabled until the end of current frame. // Set the two to 1 # Endif /* Panel control */ # If defined (FIG) If (pdata-> backlight_on) Pdata-> backlight_on (pdev ); # Elif defined (config_fb_initi_lte480wv) Lcddebug ("% s, pdata-> backlight_onoff: % P \ n", _ FUNC __, pdata-> backlight_onoff ); // If conditions are met, call lte480wv_backlight_onoff to enable the backlight. If (pdata-> backlight_onoff) Pdata-> backlight_onoff (pdev, 1 ); # Endif # If! Defined (config_fb_89c_dummylcd) Lcddebug ("% s, pdata-> reset_ LCD: % P \ n", _ FUNC __, pdata-> reset_ LCD ); // If conditions are met, call the lte480wv_reset_ LCD function. The gph0_6 pin is the reset pin of the LCD. // We can see that in the lte480wv_reset_ LCD function, gpio_request is called first. The first parameter is used to pass the macro s5pc11x_gph0 (6), and this parameter is used to directly index a static array. // Static struct gpio_desc [arch_nr_gpios] Find the struct gpio_chip Member Address of the corresponding gpio_desc element in the array to determine whether it is a function pointer // Chip-> whether the request is null. If yes, call this function. In our platform, this function does not exist. The second parameter is the label value. If the kernel option config_debug_fs // If it is true, the struct gpio_desc contains the member label. You need to assign parameter 2 to it, and vice versa. // // Call gpio_direction_output (unsigned gpio, int value). Similarly, use the s5pc11x_gph0 (6) parameter to locate the corresponding struct gpio_chip and determine the Member. // Whether the direction_output function pointer is null. If it is not null, status = chip-> direction_output (chip, gpio, value) is called. The function is actually called. // Gpio_direction_output (s5pc11x_gph0 (6), 1), set the pin s5pc11x_gph0 (6) (e020_0c00) to output, and output DAT to 1 // Mdelay (10); delay: 10 ms // Call the function gpio_set_value (s5pc11x_gph0 (6), 0) # Define gpio_set_value _ gpio_set_value _ Gpio_set_value (unsigned gpio, int value) // driver/gpio/gpiolib. c Find the corresponding struct gpio_chip through the first parameter s5pc11x_gph0 (6) Call the struct Member, function pointer void (* Set) (struct gpio_chip * chip, Unsigned offset, int value ); Set points to the function initi_gpiolib_set. This function is located in/ARCH/ARM/plat-s3c/pgio. C. The data register written to gph0 (6) In this function is 0. // From this we can see that a low level is sent to gph0 (6 ). // Mdelay (10) // Call the function gpio_set_value (s5pc11x_gph0 (6), 1) at a high level // Mdelay (10) // Gpio_free (s5pc11x_gph0 (6); release this pin
If (pdata-> reset_ LCD) Pdata-> reset_ LCD (pdev ); # Endif Lcddebug ("% s, fbdev LCD init LDI: % P \ n", _ FUNC __, fbdev-> LCD-> init_ldi ); If (fbdev-> LCD-> init_ldi) Fbdev-> LCD-> init_ldi (); /////////////////////////////////// We continue to return to the main program to view the probe function. /////////////////////////////// /* Prepare memory */ Lcddebug ("% s, s3cfb_alloc_framebuffer \ n" ,__ func __); If (s3cfb_alloc_framebuffer ()) Goto err_alloc; ************************************* Static int s3cfb_alloc_framebuffer (void) Analysis Struct initi_platform_fb * pdata = to_fb_plat (fbdev-> Dev ); Int ret, I; /* Alloc for framebuffers */ Fbdev-> Fb = (struct fb_info **) kmalloc (pdata-> nr_wins *\ Sizeof (struct fb_info *), gfp_kernel ); // Allocate the space of the FB Member of the Global struct s3cfb_global fbdev according to the number of windows set by the platform data. The Fb type is struct fb_info ** // The second-level pointer space is allocated here. If (! Fbdev-> FB) {// handle errors Err ("not enough memory \ n "); Ret =-enomem; Goto err_alloc; } /* Alloc for each framebuffer */ // Use the pdata-> nr_wins value to control the external loop. The value is 5. For (I = 0; I <pdata-> nr_wins; I ++ ){ Fbdev-> FB [I] = framebuffer_alloc (sizeof (struct s3cfb_window ),\ Fbdev-> Dev ); // The type of fbdev-> FB [I] Is struct fb_info *. Call the framebuffer_alloc function to obtain the starting address of the allocated struct fb_info space. // Framebuffer_alloc Function Analysis * Framebuffer_alloc-creates a new frame buffer INFO structure * * @ Size: size of driver private data, can be zero * @ Dev: pointer to the device for this FB, this can be null * * Creates a new frame buffer INFO structure. Also reserves @ size bytes * For Driver private data (Info-> par). Info-> PAR (if any) will be * Aligned to sizeof (long ). * * Returns the new structure, or null if an error occured. Here sizeof (struct s3cfb_window) = 136 sizeof (struct fb_info) = 596 A total of 136 + 596 bytes of memory space is allocated. Info-> par points to the drive private data struct s3cfb_window If (! Fbdev-> FB [I]) {// if the space allocation fails Err ("not enough memory \ n "); Ret =-enomem; Goto err_alloc_fb; } Ret = s3cfb_init_fbinfo (I); // initialize the corresponding struct fb_info If (RET ){ Err ("failed to allocate memory for FB % d \ n", I ); Ret =-enomem; Goto err_alloc_fb; } If (I = pdata-> default_win ){ If (s3cfb_map_video_memory (fbdev-> FB [I]) { Err ("failed to map video memory "\ "For default window (% d) \ n", I ); Ret =-enomem; Goto err_alloc_fb; } // Here, the default win is 0, so you need to map the first win memory space to the kernel virtual address space. } } ************************************* Lcddebug ("% s, s3cfb_alloc_framebuffer done \ n" ,__ func __); If (s3cfb_register_framebuffer ()) Goto err_alloc; **************************************** Next, let's look at the s3cfb_register_framebuffer function. This function is mainly used to register five windows by calling register_framebuffer. Macro defines config_framebuffer_console as true. This configuration item is in the kconfig file of the kernel Config framebuffer_console Tristate "framebuffer console support" Depends on FB Select CRC32 Help Low-level framebuffer-based console driver. The konfig file is located in/Drivers/Video/console/kconfig. Int s3cfb_register_framebuffer (void) { Struct initi_platform_fb * pdata = to_fb_plat (fbdev-> Dev ); Int ret, I; For (I = 0; I <pdata-> nr_wins; I ++ ){ Ret = register_framebuffer (fbdev-> FB [I]); If (RET ){ Err ("failed to register framebuffer device \ n "); Return-einval; } # Ifndef config_framebuffer_console If (I = pdata-> default_win ){ S3cfb_check_var (& fbdev-> FB [I]-> var, fbdev-> FB [I]); S3cfb_set_par (fbdev-> FB [I]); S3cfb_draw_logo (fbdev-> FB [I]); } # Endif } Return 0; } |