s5p4418显示驱动

arch/arm/plat-s5p4418/drone2/include/Cfg_main.h

有关显示的定义:

/*------------------------------------------------------------------------------

* 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时序图有关:

s5p4418显示驱动_第1张图片

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个周期后开始下一行。

s5p4418显示驱动_第2张图片

 

 

arch/arm/plat-s5p4418/dron2/device.c

/*------------------------------------------------------------------------------

* 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 */

arch/arm/plat-s5p4418/dron2/dev-display.c

这个文件是为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 */

arch/arm/mach-s5p4418/soc/display_lcd.c

它是一个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

},

};

arch/arm/mach-s5p4418/soc/display.c

此文件内部有一组显示结构,它将所有显示有关的设备(包括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));

}

 

nxp-fb.c

这个文件就是与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;

}

 

你可能感兴趣的:([驱动开发])