//设置寄存器0xF800_0130(Video Interrupt Control 0 Register) [16:15]位为01 = VSYNC,这两位可能的值为00 = BACK Porch,10 = ACTIVE
int s3cfb_set_vsync_interrupt(struct s3cfb_global *ctrl, int enable)
//设置寄存器0xF800_0130 enable或者disable第【12】位和第【0】位,分别表示
//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);
//设置寄存器0xF800_0000 的第[28:26]位,这三位所代表的意义是
//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
//设置寄存器0xF800_0008 [15:14] [13:12]位,这些代表的意义
// Reserved [15:14] Reserved.
// Note: This bit should be 1.
//TVFORMATSEL [13:12] Specifies the output format of YUV data.
// 00 = Reserved
// 01 = YUV422
// 1x = YUV444
// 将这四位都设置为0
s3cfb_set_display_mode(fbdev);
//设置寄存器0xF800_0000 的第[18][17]位,这两位所代表的意义是
// 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.
//这是设置这两位均为0 MODE_RGB_P = 0,
MODE_BGR_P = 1,
MODE_RGB_S = 2,
MODE_BGR_S = 3,
s3cfb_set_polarity(fbdev);
//设置寄存器xF800_0004, 第[7],[6],[5],[4] 位,这四位代表的意思是
//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);
//设置寄存器0xF800_0010 32位分别表示
//VBPDE [31:24] Vertical back porch specifies the number of inactive lines at the
// 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 the
// 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.
//设置寄存器0xF800_0014, 32位分别表示
//VFPDE [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 of
// HSYNC pulse by counting the number of VCLK.
s3cfb_set_lcd_size(fbdev);
//设置寄存器0xF800_0018, 22位有效位分别表示
//LINEVAL [21:11] Determines the vertical size of display. In the Interlace mode,
// (LINEVAL + 1) should 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;
}
我们继续看probe函数
/
LCDDEBUG("%s, s3cfb init global\n", __func__);
s3cfb_init_global();
LCDDEBUG("%s, s3cfb init global done\n", __func__);
#if !defined(CONFIG_FB_S3C_DUMMYLCD)
s3cfb_display_on(fbdev);
//设置寄存器0xF800_0000 的第[1][0]为,这两位分别表示
//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.
//这里设置这两位为1
#endif
/* panel control */
#if defined(CONFIG_FB_S3C_TL2796)
if (pdata->backlight_on)
pdata->backlight_on(pdev);
#elif defined(CONFIG_FB_S3C_LTE480WV)
LCDDEBUG("%s, pdata->backlight_onoff:%p\n", __func__, pdata->backlight_onoff);
//如果满足条件, 调用lte480wv_backlight_onoff,打开背光
if (pdata->backlight_onoff)
pdata->backlight_onoff(pdev, 1);
#endif
#if !defined(CONFIG_FB_S3C_DUMMYLCD)
LCDDEBUG("%s, pdata->reset_lcd:%p\n", __func__, pdata->reset_lcd);
//如果满足条件,调用lte480wv_reset_lcd函数, GPH0_6脚是LCD的 reset引脚
//我们可以看到在函数lte480wv_reset_lcd中首先调用gpio_request,第一个参数传递宏S5PC11X_GPH0(6),用这个参数直接索引一个静态数组
//static struct gpio_desc gpio_desc[ARCH_NR_GPIOS] 在数组中找到相应gpio_desc元素的struct gpio_chip成员地址,判断是否函数指针
//chip->request是否为空,如果存在则调用此函数,在我们的平台中,这个函数并不存在,第二个参数是label的值,如果内核选项CONFIG_DEBUG_FS
//为真,那么结构体struct gpio_desc包含成员Label,需要将参数二赋值给它,反之为空
//
//调用gpio_direction_output(unsigned gpio, int value),此函数同样,利用参数一S5PC11X_GPH0(6),找到相应的struct gpio_chip,判断成员
//direction_output函数指针是否为空,不为空则调用此函数status = chip->direction_output(chip, gpio, value);实际调用函数
//gpio_direction_output(S5PC11X_GPH0(6), 1), 配置引脚S5PC11X_GPH0(6) (E020_0C00)为output,输出dat为1
//mdelay(10);延时10ms
//调用函数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
通过第一个参数S5PC11X_GPH0(6)找到相应的结构体struct gpio_chip
调用结构体的成员,函数指针void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
set指向函数s3c_gpiolib_set 这个函数位于/arch/arm/plat-s3c/pgio.c
这个函数写GPH0(6)的数据寄存器为0
//由此可知这里向GPH0(6)发一个低电平
//mdelay(10)
//调用函数gpio_set_value(S5PC11X_GPH0(6), 1)在发一个高电平
//mdelay(10)
//gpio_free(S5PC11X_GPH0(6)); 释放该引脚
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();
///
我们继续返回主程序看probe函数
///
/* prepare memory */
LCDDEBUG(" %s, s3cfb_alloc_framebuffer \n",__func__);
if (s3cfb_alloc_framebuffer())
goto err_alloc;
*************************************
static int s3cfb_alloc_framebuffer(void) 分析
struct s3c_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);
//按照平台数据设置的window数分配全局结构体struct s3cfb_global fbdev的成员fb的空间,fb的类型是struct fb_info**
//这里分配的是二级指针的空间
if (!fbdev->fb){ //错误处理
err("not enough memory\n");
ret = -ENOMEM;
goto err_alloc;
}
/* alloc for each framebuffer */
//外循环用pdata->nr_wins的值控制,这里等于5
for (i = 0; i < pdata->nr_wins; i++) {
fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window), \
fbdev->dev);
//fbdev->fb[i]的类型为struct fb_info*,调用函数framebuffer_alloc得到分配的struct fb_info空间的起始地址
// framebuffer_alloc 函数分析
* 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.
这里sizeof(struct s3cfb_window)=136 sizeof(struct fb_info)=596
一共分配了136+596 bytes内存空间,info->par指向驱动私有数据struct s3cfb_window
if (!fbdev->fb[i]) { //如果空间分配失败
err("not enough memory\n");
ret = -ENOMEM;
goto err_alloc_fb;
}
ret = s3cfb_init_fbinfo(i);//初始化相应的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;
}//这里默认的win是0,所以需要把第一个win的Memory空间映射到内核虚拟地址空间
}
}
*************************************
LCDDEBUG(" %s, s3cfb_alloc_framebuffer done\n",__func__);
if (s3cfb_register_framebuffer())
goto err_alloc;
****************************************
下面我们看s3cfb_register_framebuffer函数,这个函数主要是通过调用register_framebuffer实现5个window的注册
宏定义CONFIG_FRAMEBUFFER_CONSOLE 为真, 该配置项在内核的kconfig文件中为
config FRAMEBUFFER_CONSOLE
tristate "Framebuffer Console support"
depends on FB
select CRC32
help
Low-level framebuffer-based console driver.
这个konfig文件位于/drivers/video/console/kconfig
int s3cfb_register_framebuffer(void)
{
struct s3c_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;
}