有关显示的定义:
/*------------------------------------------------------------------------------
* Display (DPC and MLC)
*/
/* Primary */
#define CFG_DISP_PRI_SCREEN_LAYER 0
#define CFG_DISP_PRI_SCREEN_RGB_FORMAT MLC_RGBFMT_A8R8G8B8
#define CFG_DISP_PRI_SCREEN_PIXEL_BYTE 4
#define CFG_DISP_PRI_SCREEN_COLOR_KEY 0x090909
#define CFG_DISP_PRI_VIDEO_PRIORITY 2 // 0, 1, 2, 3
#define CFG_DISP_PRI_BACK_GROUND_COLOR 0x000000
#define CFG_DISP_PRI_MLC_INTERLACE CFALSE
#define CFG_DISP_PRI_LCD_WIDTH_MM 154
#define CFG_DISP_PRI_LCD_HEIGHT_MM 85
#define CFG_DISP_PRI_RESOL_WIDTH 480 // X Resolution
#define CFG_DISP_PRI_RESOL_HEIGHT 800 // Y Resolution
红色的部分即对应驱动代码中的struct disp_vsync_info结构。这此数据在dev-display.c中设置。
#define CFG_DISP_PRI_HSYNC_SYNC_WIDTH 1
#define CFG_DISP_PRI_HSYNC_BACK_PORCH 7
#define CFG_DISP_PRI_HSYNC_FRONT_PORCH 8
#define CFG_DISP_PRI_HSYNC_ACTIVE_HIGH CTRUE
#define CFG_DISP_PRI_VSYNC_SYNC_WIDTH 1
#define CFG_DISP_PRI_VSYNC_BACK_PORCH 7
#define CFG_DISP_PRI_VSYNC_FRONT_PORCH 8
#define CFG_DISP_PRI_VSYNC_ACTIVE_HIGH CTRUE
#define CFG_DISP_PRI_CLKGEN0_SOURCE DPC_VCLK_SRC_PLL2
#define CFG_DISP_PRI_CLKGEN0_DIV 40// even divide
#define CFG_DISP_PRI_CLKGEN0_DELAY 0
#define CFG_DISP_PRI_CLKGEN0_INVERT 0
#define CFG_DISP_PRI_CLKGEN1_SOURCE DPC_VCLK_SRC_VCLK2
#define CFG_DISP_PRI_CLKGEN1_DIV 2
#define CFG_DISP_PRI_CLKGEN1_DELAY 0
#define CFG_DISP_PRI_CLKGEN1_INVERT 0
#define CFG_DISP_PRI_CLKSEL1_SELECT 0
#define CFG_DISP_PRI_PADCLKSEL DPC_PADCLKSEL_VCLK /* VCLK=CLKGEN1, VCLK12=CLKGEN0 */
#define CFG_DISP_PRI_PIXEL_CLOCK 800000000/CFG_DISP_PRI_CLKGEN0_DIV
#define CFG_DISP_PRI_OUT_SWAPRB CFALSE
#define CFG_DISP_PRI_OUT_FORMAT DPC_FORMAT_RGB888
#define CFG_DISP_PRI_OUT_YCORDER DPC_YCORDER_CbYCrY
#define CFG_DISP_PRI_OUT_INTERLACE CFALSE
#define CFG_DISP_PRI_OUT_INVERT_FIELD CFALSE
#define CFG_DISP_LCD_MPY_TYPE 0
这个表和LCD时序图有关:
VCLK表示LCD时钟频率。
HSYNC表示行同步,每个时钟周期表示扫描1行。
VSYNC表示帧同步,每个时钟周期表示扫描完1帧。
LINEVAL表示LCD垂直宽度。
HOZVAL表示LCD水平宽度。
LCD帧扫描流程:当VSYNC(脉冲宽度为VSPW+1)上升沿到来后经历VBPD+1个时钟周期后开始行扫描,VDEN表示行有效,经历LINEVAL+1个行信号后行扫描结束,再经历VFPD+1时钟后进行下一个帧信号。
行扫描流程:当HSYNC(脉冲宽度为HSPW+1)上升沿后HBPD+1周期后开始扫描每个点,经历HOZVAL+1个点后行扫描结束,再经历HFPD+1个周期后开始下一行。
/*------------------------------------------------------------------------------
* DISPLAY (LVDS) / FB
*/
#if defined (CONFIG_FB_NXP)
#if defined (CONFIG_FB0_NXP)
static struct nxp_fb_plat_data fb0_plat_data = {
.module = CONFIG_FB0_NXP_DISPOUT,
.layer = CFG_DISP_PRI_SCREEN_LAYER,
.format = CFG_DISP_PRI_SCREEN_RGB_FORMAT,
.bgcolor = CFG_DISP_PRI_BACK_GROUND_COLOR,
.bitperpixel = CFG_DISP_PRI_SCREEN_PIXEL_BYTE * 8,
.x_resol = CFG_DISP_PRI_RESOL_WIDTH,
.y_resol = CFG_DISP_PRI_RESOL_HEIGHT,
#ifdef CONFIG_ANDROID
.buffers = 3,
.skip_pan_vsync = 1,
#else
.buffers = 2,
#endif
.lcd_with_mm = CFG_DISP_PRI_LCD_WIDTH_MM, /* 152.4 */
.lcd_height_mm = CFG_DISP_PRI_LCD_HEIGHT_MM, /* 91.44 */
};
static struct platform_device fb0_device = {
.name = DEV_NAME_FB, //“nxp-fb”
.id = 0, /* FB device node num */
.dev = {
.coherent_dma_mask = 0xffffffffUL, /* for DMA allocate */
.platform_data = &fb0_plat_data
},
};
#endif
static struct platform_device *fb_devices[] = {
#if defined (CONFIG_FB0_NXP)
&fb0_device,
#endif
};
#endif /* CONFIG_FB_NXP */
这个文件是为display_lcd.c(见下面,这是platform_driver)服务的,这里定义platform_data。
/*------------------------------------------------------------------------------
* LCD platform device
*/
#if defined (CONFIG_NXP_DISPLAY_LCD)
static struct disp_vsync_info __lcd_vsync = {
/* default parameters refer to cfg_main.h */
#if defined(CFG_DISP_PRI_RESOL_WIDTH) && defined(CFG_DISP_PRI_RESOL_HEIGHT)
.h_active_len = CFG_DISP_PRI_RESOL_WIDTH,
.h_sync_width = CFG_DISP_PRI_HSYNC_SYNC_WIDTH,
.h_back_porch = CFG_DISP_PRI_HSYNC_BACK_PORCH,
.h_front_porch = CFG_DISP_PRI_HSYNC_FRONT_PORCH,
.h_sync_invert = CFG_DISP_PRI_HSYNC_ACTIVE_HIGH,
.v_active_len = CFG_DISP_PRI_RESOL_HEIGHT,
.v_sync_width = CFG_DISP_PRI_VSYNC_SYNC_WIDTH,
.v_back_porch = CFG_DISP_PRI_VSYNC_BACK_PORCH,
.v_front_porch = CFG_DISP_PRI_VSYNC_FRONT_PORCH,
.v_sync_invert = CFG_DISP_PRI_VSYNC_ACTIVE_HIGH,
.pixel_clock_hz = CFG_DISP_PRI_PIXEL_CLOCK,
.clk_src_lv0 = CFG_DISP_PRI_CLKGEN0_SOURCE,
.clk_div_lv0 = CFG_DISP_PRI_CLKGEN0_DIV,
.clk_src_lv1 = CFG_DISP_PRI_CLKGEN1_SOURCE,
.clk_div_lv1 = CFG_DISP_PRI_CLKGEN1_DIV,
#endif
};
static struct disp_lcd_param __lcd_devpar; //注意,没有做初始化,但在下面的函数__disp_lcd_dev_data赋了值。
static struct nxp_lcd_plat_data lcd_data = {
.display_in = DISPLAY_INPUT(CONFIG_NXP_DISPLAY_LCD_IN),
.display_dev = DISP_DEVICE_LCD,
.vsync = &__lcd_vsync,
.dev_param = (union disp_dev_param*)&__lcd_devpar,
};
//此结构没有被使用。
static struct platform_device lcd_device = {
.name = DEV_NAME_LCD, // "nxp-lcd" //arch/arm/plat-s5p4418/soc/display_lcd.c是此名称的platform_driver驱动
.id = -1,
.dev = {
.platform_data = &lcd_data
},
};
//此函数的作用是将dev_par, sgpar的参数都复制到本地lcd_data实体,并将lcd_data.vsync复制到输出参数*vsync。lcd_data实体是arch/arm/mach-s5p4418/display_lcd.c中的platform_driver中的platform_data
static void __disp_lcd_dev_data(struct disp_vsync_info *vsync,
void *dev_par, struct disp_syncgen_par *sgpar)
{
struct nxp_lcd_plat_data *plcd = &lcd_data; //lcd_data是本地数据。
struct disp_lcd_param *dst = (struct disp_lcd_param *)plcd->dev_param;
struct disp_lcd_param *src = dev_par;
if (src) {
SET_PARAM(src, dst, lcd_format);
SET_PARAM(src, dst, lcd_mpu_type);
SET_PARAM(src, dst, invert_field);
SET_PARAM(src, dst, swap_RB);
SET_PARAM(src, dst, yc_order);
SET_PARAM(src, dst, lcd_init);
SET_PARAM(src, dst, lcd_exit);
}
if (sgpar)
plcd->sync_gen = sgpar;
SET_VSYNC_INFO(vsync, plcd->vsync);
}
#else
#define __disp_lcd_dev_data(s, p, g)
#endif /* LCD */
它是一个platform_driver,注册的platform_driver是
#define DEV_NAME_LCD "nxp-lcd"
它为display.c(见下面)服务,注册disp_process_ops结构。
static struct disp_process_ops lcd_ops = {
.set_vsync = lcd_set_vsync,
.get_vsync = lcd_get_vsync,
.enable = lcd_enable,
.stat_enable= lcd_stat_enable,
.suspend = lcd_suspend,
.resume = lcd_resume,
};
static int lcd_probe(struct platform_device *pdev)
{
struct nxp_lcd_plat_data *plat = pdev->dev.platform_data;
struct disp_lcd_param *plcd;
struct disp_vsync_info *psync;
struct disp_syncgen_par *sgpar;
int device = DISP_DEVICE_LCD;
int input;
RET_ASSERT_VAL(plat, -EINVAL);
RET_ASSERT_VAL(plat->display_in == DISP_DEVICE_SYNCGEN0 ||
plat->display_in == DISP_DEVICE_SYNCGEN1 ||
plat->display_dev == DISP_DEVICE_LCD ||
plat->display_in == DISP_DEVICE_RESCONV, -EINVAL);
RET_ASSERT_VAL(plat->vsync, -EINVAL);
plcd = kzalloc(sizeof(*plcd), GFP_KERNEL);
RET_ASSERT_VAL(plcd, -EINVAL);
if (plat->dev_param)
memcpy(plcd, plat->dev_param, sizeof(*plcd));
sgpar = plat->sync_gen;
psync = plat->vsync;
input = plat->display_in;//在dev-display.c中设置lcd_data->display_in= DISP_DEVICE_SYNCGEN0
nxp_soc_disp_register_proc_ops(device, &lcd_ops); //此注册函数在同目录下的display.c中
nxp_soc_disp_device_connect_to(device, input, psync);
nxp_soc_disp_device_set_dev_param(device, plcd);
if (sgpar &&
(input == DISP_DEVICE_SYNCGEN0 ||
input == DISP_DEVICE_SYNCGEN1))
nxp_soc_disp_device_set_sync_param(input, sgpar);
printk("LCD : [%d]=%s connect to [%d]=%s\n",
device, dev_to_str(device), input, dev_to_str(input));
return 0;
}
第一句:struct nxp_lcd_plat_data *plat = pdev->dev.platform_data;
对应dev-display.c中:
static struct platform_device lcd_device = { 但代码中没有找到驱动注册此结构的代码,为何??
.name = DEV_NAME_LCD,
.id = -1,
.dev = {
.platform_data = &lcd_data
},
};
此文件内部有一组显示结构,它将所有显示有关的设备(包括LCD)定义了一个内部数据结构数组:
static struct disp_process_dev device_dev[] = {
[0] = { .dev_id = DISP_DEVICE_RESCONV , .name = "RESCONV" , .list = LIST_INIT(0), .lock = LOCK_INIT(0)},
[1] = { .dev_id = DISP_DEVICE_LCD , .name = "LCD" , .list = LIST_INIT(1), .lock = LOCK_INIT(1)},
[2] = { .dev_id = DISP_DEVICE_HDMI , .name = "HDMI" , .list = LIST_INIT(2), .lock = LOCK_INIT(2)},
[3] = { .dev_id = DISP_DEVICE_MIPI , .name = "MiPi" , .list = LIST_INIT(3), .lock = LOCK_INIT(3)},
[4] = { .dev_id = DISP_DEVICE_LVDS , .name = "LVDS" , .list = LIST_INIT(4), .lock = LOCK_INIT(4)},
[5] = { .dev_id = DISP_DEVICE_SYNCGEN0, .name = "SYNCGEN0", .list = LIST_INIT(5), .lock = LOCK_INIT(5)},
[6] = { .dev_id = DISP_DEVICE_SYNCGEN1, .name = "SYNCGEN1", .list = LIST_INIT(6), .lock = LOCK_INIT(6)},
};
#define DEVICE_SIZE ARRAY_SIZE(device_dev)
static struct kobject *kobj_syncgen = NULL;
static inline void *get_display_ptr(enum disp_dev_type device) //根据参数得到指定的结构指针
{
return (&device_dev[device]);
}
const char * dev_to_str(enum disp_dev_type device) //打印出名称
{
struct disp_process_dev *pdev = get_display_ptr(device);
return pdev->name;
}
每一个显示设备都是disp_process_dev结构:
/* display device instance (syncgen, lvds, lcd,..) */
struct disp_process_dev {
const char *name;
int dev_id;
int dev_in;
int dev_out;
unsigned int save_addr;
unsigned int base_addr;
struct list_head list;
unsigned int status;
spinlock_t lock;
struct disp_vsync_info vsync;
struct disp_syncgen_par sync_gen;
struct disp_process_ops *disp_ops; //即是上面display_lcd.c中定义的disp_process_ops
void * dev_param;
void * dev_info;
void * priv;
};
分析display_lcd.c中使用的第一个函数nxp_soc_disp_register_proc_ops:
此函数将ops赋给内部数据device_dev[1]->disp_ops。
void nxp_soc_disp_register_proc_ops(enum disp_dev_type device, struct disp_process_ops *ops)
{
struct disp_process_dev *pdev = get_display_ptr(device);
RET_ASSERT(DEVICE_SIZE > device);
RET_ASSERT(device == pdev->dev_id);
if (get_display_ops(device))
printk(KERN_ERR "Warn , %s operation will be replaced \n", dev_to_str(device));
spin_lock(&pdev->lock);
/* set device info */
set_display_ops (device, ops);
spin_unlock(&pdev->lock);
printk(KERN_INFO "Display %s register operation \n", dev_to_str(device));
}
这个文件就是与frambuffer驱动fbmem.c对接的代码。在probe时调用了register_framebuffer(info);info的数据结构定义如下:
struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* open/release/ioctl函数使用的锁 */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* 可变参数*/
struct fb_fix_screeninfo fix; /* 固定参数 */
struct fb_monspecs monspecs; /* 显示器标准 */
struct work_struct queue; /* Framebuffer event queue 事件队列*/
struct fb_pixmap pixmap; /* Image hardware mapper图像硬件mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper光标硬件mapper */
struct fb_cmap cmap; /* Current cmap目前颜色表 */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops;//帧缓冲操作函数fb_ops
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address 虚拟基地址,大小即长*宽*每像素占字句字节数*buff个数。*/
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ io remapped虚拟内存大小。
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend 硬件状态,如挂起*/
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
nxp_fb_probe的函数做的工作主要是初始化struct fb_info,并调用register_framebuffer(info);
static int nxp_fb_probe(struct platform_device *pdev)
{
struct nxp_fb_plat_data *plat = pdev->dev.platform_data; // arch/arm/plat-s5p4418/dron2/device.c中定义了”nxp-fb”设备的platform_data,见上面的代码分析。
struct fb_info *info = NULL;
#ifdef CONFIG_FB_NXP_ION_MEM
struct nxp_fb_device *fbdev;
struct nxp_fb_param *fbpar;
#endif
int i = 0, ret = 0;
pr_debug("\n%s (name=%s, id=%d)\n", __func__, dev_name(&pdev->dev), pdev->id);
/* allocate fb_info and init */
info = nxp_fb_init_fb(pdev->id, &pdev->dev);
if(! info) {
ret = -ENOMEM;
goto err_fb;
}
ret = nxp_fb_setup_param(pdev->id, info, plat);
if (0 > ret)
goto err_map;
nxp_fb_setup_info(info);
#ifdef CONFIG_FB_NXP_ION_MEM
fbpar = info->par;
fbdev = &fbpar->fb_dev;
fbdev->dev = &pdev->dev;
ret = nxp_fb_setup_ion(&fbpar->fb_dev.dma_buf_data);
if (ret) {
printk(KERN_ERR "Fail to setup ion\n");
goto err_map;
}
#endif
/* allocate frame buffer memory from here */
ret = nxp_fb_alloc_mem(info);
if(ret) {
printk(KERN_ERR "Fail, unable to allcate frame buffer (%d)\n", pdev->id);
goto err_map;
}
nxp_fb_init_display(info);
/*
* device_create '/proc/fb0' & fb class
* register machine file operation to frame buffer file operation
* registered_fb[]
* (drivers/video/fbmem.c)
*/
if (pdev->id != 0) {
for (i = 0; pdev->id > i; i++) {
if (!registered_fb[i]) {
printk("FB: Reserve dev/node [%d]\n", i);
registered_fb[i] = info;
}
}
}
ret = register_framebuffer(info);
if(ret < 0) {
printk(KERN_ERR "Fail, unable to register frame buffer(%d)\n", pdev->id);
goto err_reg;
}
/* register to driver data, use platform_get_drvdata */
platform_set_drvdata(pdev, info);
return ret;
err_reg:
unregister_framebuffer(info);
err_map:
nxp_fb_free_mem(info);
err_fb:
nxp_fb_exit_fb(info);
return ret;
}